#更高级别的threading不仅提供了Thread类,还提供了各种非常好用的同步机制。
#_thread模块不支持守护线程,当主线程退出时,所有子线程无论是否在工作,都会被强制退出。threading模块支持守护线程,守护线程一般时一个等待客户请求的服务器,如果没有客户提出请求,就一直等着。如果设定一个线程为守护线程,就表示这个线程不重要,在进程退出时,不用等待这个线程退出。如果主线程退出时不用等待子线程完成,就要设定这些线程的daemon属性,即在线程Thread.start()开始前,调用setDaemon()函数设定线程的daemon标志(Thread.setDaemon(True)),表示这个线程“不重要”,如果一定要等待子线程执行完成再退出主线程,就什么都不用做或显式调用Thread.setDaemon(False)以保证daemon标志为False,可以调用Thread.isDaemon()函数判断daemon标志的值。新的子线程会继承父线程的daemon标志,整个Python在所有非守护线程退出后才会结束,即进程中没有非守护线程存在时才会结束。
threading的thread类
#Thread有很多_thread模块里没有的函数,Thread对象的函数很丰富。下面创建一个Thread的实例,传给他一个函数。示例如下:
1 #!/usr/bin/python3 2 #-*-coding:UTF-8-*- 3 #threading的thread类 4 5 import threading 6 from time import sleep 7 from datetime import datetime 8 9 loops=[4,2]10 date_time_format='%y-%M-%d %H:%M:%S'11 12 def date_time_str(date_time):13 return datetime.strftime(date_time,date_time_format)14 15 def loop(n_loop,n_sec):16 print('线程(',n_loop,')开始执行:',date_time_str(datetime.now()),',先休眠(',n_sec,')秒')17 sleep(n_sec)18 print('线程(',n_loop,')休眠结束,结束于:',date_time_str(datetime.now()))19 20 def main():21 print('---所有线程开始执行:',date_time_str(datetime.now()))22 threads=[]23 n_loops=range(len(loops))24 25 for i in n_loops:26 t=threading.Thread(target=loop,args=(i,loops[i]))27 threads.append(t)28 29 for i in n_loops: #start threads30 threads[i].start()31 32 for i in n_loops: #wait for all33 threads[i].join() #thread to finish34 35 print('---所有线程执行结束于:',date_time_str(datetime.now()))36 37 if __name__=='__main__':38 main()
#执行结果如下:
1 D:\Pythonworkspace>python threading_thread.py2 ---所有线程开始执行: 18-31-30 09:31:543 线程( 0 )开始执行: 18-31-30 09:31:54 ,先休眠( 4 )秒4 线程( 1 )开始执行: 18-31-30 09:31:54 ,先休眠( 2 )秒5 线程( 1 )休眠结束,结束于: 18-31-30 09:31:566 线程( 0 )休眠结束,结束于: 18-31-30 09:31:587 ---所有线程执行结束于: 18-31-30 09:31:58
#由执行结果我们看到,实例化一个Thread(调用Thread())与调用_thread.start_new_thread()最大的区别是新的线程不会立即开始。创建线程对象却不想马上开始运行线程时,Thread是一个很有用的同步特性。所有线程都创建之后,再一起调用start()函数启动,而不是每创建一个线程就启动。而且不用管理一堆锁的状态(分配锁、获得锁、释放锁、检查锁的等状态),只要简单对每个线程调用join()主线程,等待子线程结束即可。join()还可以设置timeout参数,即主线程的超时时间。
#join()的另一个比较重要的方面是可以完全不用调用。一旦线程启动,就会一直运行,直到线程的函数结束并退出为止。如果主线程除了等线程结束外,还有其他事情要做,就不用调用join(),只有在等待线程结束时才调用。
#我们再看示例,创建一个Thread的实例,并传给它一个可调用的类对象。代码如下:
1 #!/usr/bin/python3 2 #-*-coding:UTF-8-*- 3 #threading_join() 4 5 import threading 6 from time import sleep 7 from datetime import datetime 8 9 loops=[4,2]10 date_time_format='%y-%M-%d %H:%M:%S'11 12 class ThreadFunc(object):13 def __init__(self,func,args,name=''):14 self.name=name15 self.func=func16 self.args=args17 18 def __call__(self):19 self.func(*self.args)20 21 def date_time_str(date_time):22 return datetime.strftime(date_time,date_time_format)23 24 def loop(n_loop,n_sec):25 print('线程(',n_loop,')开始执行:',date_time_str(datetime.now()),',先休眠(',n_sec,')秒')26 sleep(n_sec)27 print('线程(',n_loop,')休眠结束,结束于:',date_time_str(datetime.now()))28 29 def main():30 print('---所有线程开始执行:',date_time_str(datetime.now()))31 threads=[]32 nloops=range(len(loops))33 34 for i in nloops:35 t=threading.Thread(target=ThreadFunc(loop,(i,loops[i]),loop.__name__))36 threads.append(t)37 38 for i in nloops: #start all threads39 threads[i].start()40 41 for i in nloops: #wait for completion42 threads[i].join()43 44 print('---所有线程执行结束于:',date_time_str(datetime.now()))45 46 if __name__=='__main__':47 main()
#执行结果如下:
1 D:\Pythonworkspace>python threading_join().py2 ---所有线程开始执行: 18-30-30 11:30:263 线程( 0 )开始执行: 18-30-30 11:30:26 ,先休眠( 4 )秒4 线程( 1 )开始执行: 18-30-30 11:30:26 ,先休眠( 2 )秒5 线程( 1 )休眠结束,结束于: 18-30-30 11:30:286 线程( 0 )休眠结束,结束于: 18-30-30 11:30:307 ---所有线程执行结束于: 18-30-30 11:30:30
#由执行结果看到,与传一个函数很相似的一个方法是,在创建线程时,传一个可调用的类的实例供线程启动时执行,这是多线程编程的一个面向对象的方法。相对于一个或多个函数来说,类对象可以使用类的强大功能。创建线程时,Thread对象会调用ThreadFunc对象,这时会用到一个特殊函数__call__()。由于已经有了要用的参数,因此不用再传到Thread()的构造函数中。对于一个参数的元组,要使用self.func(*self.args)方法。
#从Thread派生一个子类,创建这个子类的实例,从上面的代码派生的代码如下:
1 #!/usr/bin/python3 2 #-*-coding:UTF-8-*- 3 #threading___call__ 4 5 import threading 6 from time import sleep 7 from datetime import datetime 8 9 loops=[4,2]10 date_time_format='%y-%M-%d %H:%M:%S'11 12 class MyThread(threading.Thread):13 def __init__(self,func,args,name=''):14 threading.Thread.__init__(self)15 self.name=name16 self.func=func17 self.args=args18 19 def getResult(self):20 return self.res21 22 def run(self):23 print('starting',self.name,'at:',date_time_str(datetime.now()))24 self.res=self.func(*self.args)25 print(self.name,'finished at:',date_time_str(datetime.now()))26 27 def date_time_str(date_time):28 return datetime.strftime(date_time,date_time_format)29 30 def loop(n_loop,n_sec):31 print('线程(',n_loop,')开始执行:',date_time_str(datetime.now()),',先休眠(',n_sec,')秒')32 sleep(n_sec)33 print('线程(',n_loop,')休眠结束,结束于:',date_time_str(datetime.now()))34 35 def main():36 print('---所有线程开始执行:',date_time_str(datetime.now()))37 threads=[]38 n_loops=range(len(loops))39 40 for i in n_loops:41 t=MyThread(loop,(i,loops[i]),loop.__name__)42 threads.append(t)43 44 for i in n_loops:45 threads[i].start()46 47 for i in n_loops:48 threads[i].join()49 50 print('---所有线程执行结束于:',date_time_str(datetime.now()))51 52 if __name__=='__main__':53 main()
#执行结果如下:
1 D:\Pythonworkspace>python threading_call.py 2 ---所有线程开始执行: 18-12-30 15:12:55 3 starting loop at: 18-12-30 15:12:55 4 线程( 0 )开始执行: 18-12-30 15:12:55 ,先休眠( 4 )秒 5 starting loop at: 18-12-30 15:12:55 6 线程( 1 )开始执行: 18-12-30 15:12:55 ,先休眠( 2 )秒 7 线程( 1 )休眠结束,结束于: 18-12-30 15:12:57 8 loop finished at: 18-12-30 15:12:57 9 线程( 0 )休眠结束,结束于: 18-12-30 15:12:5910 loop finished at: 18-12-30 15:12:5911 ---所有线程执行结束于: 18-12-30 15:12:59
#由代码片段和执行结果我们看到,子类化Thread,MyThread子类的构造函数一定要先调用基类的构造函数,特殊函数__call__()在子类中,名字要改为run()。在Thread类中,加入一些用于调试的输出信息,把代码保存到MyThread模块中,并导入这个类。使用self.func()函数运行这些函数,并把结果保存到实现的self.res属性中,创建一个新函数getResult()得到结果。