为什么要用到多线程?因为在一个进程中往往会遇到同时做多样事情的情况,比如音乐播放器可以同时播放和下载,文档处理器可以同时编辑和打印,这些都是多线程的实际使用场景。如果是单线程,那么音乐播放器在下载音乐的时候就不能播放音乐,在播放音乐的时候就不能下载。为什么会有这种设计呢?假设计算机只有一个CPU,在运行多线程场景的时候,每个线程都会消耗CPU的资源,但是这些线程从时间绝对精确的角度来看,也不是同时在执行,不是同时在消耗CPU资源。记住,一个CPU在任何一个绝对精确的时刻,只能运行一个线程。之所以我们看起来多个线程是一起在执行并且消耗CPU资源,是因为CPU内部在进行着线程间执行顺序的高速切换(比如在1纳秒之前运行着A线程,在1纳秒之后A线程停顿运行着B线程)。这样多线程在人类的角度看起来就是同时执行着的,程序在做A工作的时候同时在做B工作。当然,如今的双核四核计算机不断出现,可以真正的实现多个线程同时在多个CPU上执行的。
关于python的多线程,要往深处研究,其实还得下一番功夫的。不过任何事情都是从简单到复杂的,我们先从简单的开始
python中要构造多线程往往要继承threading.Thread这个类,要继承这个类,首先要引入threading这个模块。
从一个最基本的例子入手,主要学习setDaemon()函数和join()函数,这两个函数实质上是对功能相反的函数。setDaemon()必须在start() 方法调用之前设置,只要父线程执行完毕,那么不管子线程有没有执行结束,整个进程都会随着父线程的结束而结束。join()函数则刚好相反,不管父线程有没有结束,在子线程完成运行之前,这个子线程的父线程将一直被阻塞。直到子线程执行完成。
import threading import time class ThreadSong(threading.Thread): def run(self): i=100 while i>0: print "the computer is playing songs %d "%(i) #time.sleep(3) i=i-1 class ThreadMovie(threading.Thread): def run(self): i=200 while i>0: print "the computer is playing movies %d "%(i) #time.sleep(5) i=i-1 if __name__=="__main__": tsong=ThreadSong() tmovie=ThreadMovie() tsong.setDaemon(True) tmovie.setDaemon(True) tsong.start() tmovie.start() print "the program has finished"
执行结果:
the computer is playing songs 100
the computer is playing songs 99
the computer is playing songs 98
the computer is playing songs 97
the computer is playing songs 96 the computer is playing movies 200 the program has finished
the computer is playing songs 95 the computer is playing movies 199
the computer is playing songs 94 the computer is playing movies 198
the computer is playing songs 93 the computer is playing movies 197
the computer is playing songs 92
从以上例子可以看出,主线程执行了“the program has finished”之后,主线程退出,与此同时,两个子线程也没有执行完成也退出了。就是因为设置了setDaemon(True)方法。
再看第二个例子,使用join()函数
import threading import time class ThreadSong(threading.Thread): def run(self): i=10 while i>0: print "the computer is playing songs %d "%(i) i=i-1 class ThreadMovie(threading.Thread): def run(self): i=5 while i>0: print "the computer is playing movies %d "%(i) i=i-1 if __name__=="__main__": tsong=ThreadSong() tmovie=ThreadMovie() tsong.start() tmovie.start() tsong.join() tmovie.join() print "the program has finished"
运行结果如下(为使结果少一些,将i的值变小了):
the computer is playing songs 10
the computer is playing songs 9
the computer is playing songs 8
the computer is playing songs 7
the computer is playing songs 6 the computer is playing movies 5
the computer is playing songs 5 the computer is playing movies 4
the computer is playing songs 4
the computer is playing songs 3 the computer is playing movies 3
the computer is playing songs 2 the computer is playing movies 2
the computer is playing songs 1 the computer is playing movies 1
the program has finished
由此可见,两个子线程是并行在运行的,主线程在最后打印出“the program has finished”。必须等到两个子线程都执行完成之后,自己才能执行完成,并退出整个进程。
这里我们可以做以下猜想,假如我们既不使用setDaemon()函数,也不使用join()函数,那么结果会是怎样的。不使用setDaemon()函数,那么即使主线程退出,那么子线程依然在执行。不使用join()函数,主线程并不会等待所有子线程执行完之后才执行。所以可以猜测即使主线程执行完了其他线程也会继续执行。
import threading import time class ThreadSong(threading.Thread): def run(self): i=10 while i>0: print "the computer is playing songs %d "%(i) #time.sleep(3) i=i-1 class ThreadMovie(threading.Thread): def run(self): i=5 while i>0: print "the computer is playing movies %d "%(i) #time.sleep(5) i=i-1 if __name__=="__main__": tsong=ThreadSong() tmovie=ThreadMovie() tsong.start() tmovie.start() print "the program has finished"
执行结果:
the computer is playing songs 10
the computer is playing songs 9
the computer is playing songs 8
the computer is playing songs 7
the computer is playing songs 6 the program has finishedthe computer is playing movies 5
the computer is playing songs 5 the computer is playing movies 4
the computer is playing songs 4 the computer is playing movies 3
the computer is playing songs 3 the computer is playing movies 2
the computer is playing songs 2
the computer is playing songs 1 the computer is playing movies 1