#更高级别的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 threads 30 threads[i].start() 31 32 for i in n_loops: #wait for all 33 threads[i].join() #thread to finish 34 35 print('---所有线程执行结束于:',date_time_str(datetime.now())) 36 37 if __name__=='__main__': 38 main()
#执行结果如下:
1 D:Pythonworkspace>python threading_thread.py 2 ---所有线程开始执行: 18-31-30 09:31:54 3 线程( 0 )开始执行: 18-31-30 09:31:54 ,先休眠( 4 )秒 4 线程( 1 )开始执行: 18-31-30 09:31:54 ,先休眠( 2 )秒 5 线程( 1 )休眠结束,结束于: 18-31-30 09:31:56 6 线程( 0 )休眠结束,结束于: 18-31-30 09:31:58 7 ---所有线程执行结束于: 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=name 15 self.func=func 16 self.args=args 17 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 threads 39 threads[i].start() 40 41 for i in nloops: #wait for completion 42 threads[i].join() 43 44 print('---所有线程执行结束于:',date_time_str(datetime.now())) 45 46 if __name__=='__main__': 47 main()
#执行结果如下:
1 D:Pythonworkspace>python threading_join().py 2 ---所有线程开始执行: 18-30-30 11:30:26 3 线程( 0 )开始执行: 18-30-30 11:30:26 ,先休眠( 4 )秒 4 线程( 1 )开始执行: 18-30-30 11:30:26 ,先休眠( 2 )秒 5 线程( 1 )休眠结束,结束于: 18-30-30 11:30:28 6 线程( 0 )休眠结束,结束于: 18-30-30 11:30:30 7 ---所有线程执行结束于: 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=name 16 self.func=func 17 self.args=args 18 19 def getResult(self): 20 return self.res 21 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:59 10 loop finished at: 18-12-30 15:12:59 11 ---所有线程执行结束于: 18-12-30 15:12:59
#由代码片段和执行结果我们看到,子类化Thread,MyThread子类的构造函数一定要先调用基类的构造函数,特殊函数__call__()在子类中,名字要改为run()。在Thread类中,加入一些用于调试的输出信息,把代码保存到MyThread模块中,并导入这个类。使用self.func()函数运行这些函数,并把结果保存到实现的self.res属性中,创建一个新函数getResult()得到结果。