1. 程式人生 > >Python學習心得(七) 深入理解threading多線程模塊

Python學習心得(七) 深入理解threading多線程模塊

沒有 必須 派生 數據結構 cti 實例 imp ads elf

  Python提供了多個模塊來支持多線程編程,包括thread、threading和queue模塊等。
thread模塊提供了基本的線程和鎖定支持;而threading模塊提供了更高級別、功能更全面的線程管理。
queue模塊,用戶可以創建一個隊列數據結構,用於在多線程之間進行共享。

核心提示:避免使用thread模塊
推薦使用更高級別的threading模塊,原因如下:
1.threading模塊更加先進,有更好的線程支持,並且thread模塊中的一些屬性會和threading模塊有沖突;
2.低級別的thread模塊擁有的同步原語很少(實際上只有一個),而threading模塊則有很多;
3.thread模塊對於進程何時退出沒有控制,當主線程結束時,所有其他線程也都強制結束,不會發出警告或者進行適當的清理;
4.threading模塊至少能確保重要的子線程在進程退出去前結束。
5.thread模塊不支持守護線程這個概念(thread:當主線程退出時,所有子線程都將終止,不管它們是否仍在工作)

一.避免使用thread模塊,但要了解其基本用法

import thread 
from time import sleep,ctime,strftime

loops = [4,2]

def loop(nloop,nsec,lock):
    print ‘start loop‘,nloop,‘at:‘,strftime(‘%Y-%m-%d %H:%M:%S‘) 
    sleep(nsec)
    print ‘loop‘,nloop,‘done,at:‘,strftime(‘%Y-%m-%d %H:%M:%S‘)
    lock.release() #釋放鎖
def main():
    print ‘starting at:‘,strftime(‘%Y-%m-%d %H:%M:%S‘)
    locks = []
    nloops = range(len(loops)) #[0, 1]
    for i in nloops:
        lock = thread.allocate_lock() #獲取LockType鎖對象
        lock.acquire() #取得每個鎖(相當於"把鎖鎖上")
        locks.append(lock) #一旦鎖被鎖上,就可以把它添加到鎖列表locks中了
        #print locks
    for i in nloops: #派生線程
        #每個線程會調用loop()函數,並傳遞循環號、睡眠時間以及用於該線程的鎖
        thread.start_new_thread(loop,(i,loops[i],locks[i]))
        sleep(1)
    for i in nloops:
        while locks[i].locked(): #暫停主線程,等待,直到所有鎖都被釋放之後才會繼續執行
            pass
    print ‘all done at:‘,strftime(‘%Y-%m-%d %H:%M:%S‘)
    
if __name__ == ‘__main__‘:
    main()

二.推薦使用更高級別的threading模塊,函數式編程法  

import threading
#threading模塊的Thread類有一個join()方法,可以讓主線程等待所有線程執行完畢

from time import sleep,strftime

loops = [4,2,2]

def loop(nloop,nsec):
    print ‘start loop‘,nloop,‘at:‘,strftime(‘%Y-%m-%d %H:%M:%S‘) 
    sleep(nsec)
    print ‘loop‘,nloop,‘done,at:‘,strftime(‘%Y-%m-%d %H:%M:%S‘)
def main():
    print ‘starting at:‘,strftime(‘%Y-%m-%d %H:%M:%S‘)
    threads = []
    nloops = range(len(loops)) #[0, 1]
    
    for i in nloops:
        #實例化Thread(調用Thread()和之前調用thread.start_new_thread()最大區別在於新線程不會立即開始執行
        t = threading.Thread(target=loop,args=(i,loops[i])) #這是個非常有用的同步功能
        threads.append(t)

    for i in nloops: #啟動線程
        #當所有線程都分配完成後,通過調用每個線程的start()方法來讓它們執行,而不是在這之前就會執行
        threads[i].start()
        
    for i in nloops: 
        #為每個線程調用join()方法,join()方法將等待線程結束,或在提供了超時時間的情況下,達到超時時間。
        threads[i].join() 
        #join()方法只有在你需要等待線程完成的時候才有用
        #join()方法其實根本不需要調用,一旦線程啟動,它們就會一直執行,直到給定的函數完成後退出。
        
    print ‘all done at:‘,strftime(‘%Y-%m-%d %H:%M:%S‘)

print loop.__name__
    
if __name__ == ‘__main__‘:
    main()

三.threading模塊的面向對象編程法,使用可調用的類 

import threading
from time import sleep,strftime

loops = [4,2]

class ThreadFunc(object): #添加ThreadFunc類
    def __init__(self,func,args,name = ‘‘): #構造方法設定函數自身,函數參數及函數名的字符串
        self.name = name
        self.func = func
        self.args = args
    def __call__(self): #__call__特殊方法直接調用
        self.func(*self.args)

def loop(nloop,nsec):
    print ‘start loop‘,nloop,‘at:‘,strftime(‘%Y-%m-%d %H:%M:%S‘)
    sleep(nsec)
    print ‘loop‘,nloop,‘done at:‘,strftime(‘%Y-%m-%d %H:%M:%S‘)

def main():
    print ‘starting at:‘,strftime(‘%Y-%m-%d %H:%M:%S‘)
    threads = []
    nloops = range(len(loops))
    
    for i in nloops: #實例化Thread類對象
        t = threading.Thread(target=ThreadFunc(loop,(i,loops[i]),loop.__name__)) #分配線程
        threads.append(t) 
    
    for i in nloops:
        threads[i].start() #真正通過start()方法才啟動線程
                           
    
    for i in nloops:
        threads[i].join() #等待線程完成
    print ‘all done at:‘,strftime(‘%Y-%m-%d %H:%M:%S‘)   
    
if __name__ == ‘__main__‘:
    main()        

四.threading模塊面向對象擴展,派生Thread的子類,並創建子類的實例 

‘‘‘
import threading
from time import sleep,strftime

loops = [4,2]

class MyThread(threading.Thread):
    def __init__(self,func,args,name=‘‘):
        threading.Thread.__init__(self) #MyThread子類的構造方法必須先調用基類(即父類)的構造方法
        self.name = name
        self.func = func        
        self.args = args
   
    def run(self): #之前的特殊方法__call__在這裏必須要寫成run()
        self.func(*self.args)
 
def loop(nloop,nsec):
    print ‘start loop‘,nloop,‘at:‘,strftime(‘%Y-%m-%d %H:%M:%S‘)
    sleep(nsec)
    print ‘loop‘,nloop,‘done at:‘,strftime(‘%Y-%m-%d %H:%M:%S‘)

def main():
    print ‘starting at:‘,strftime(‘%Y-%m-%d %H:%M:%S‘)
    threads = []
    nloops = range(len(loops)) 
    #print nloops
    
    for i in nloops:
        t = MyThread(loop,(i,loops[i]),loop.__name__)
        threads.append(t)
    
    for i in nloops:
        threads[i].start()
    
    for i in nloops:
        threads[i].join()
        
    print ‘all done at:‘,strftime(‘%Y-%m-%d %H:%M:%S‘)
    
if __name__ == ‘__main__‘:
    main()
                   
‘‘‘

#Thread子類MyThread 
#將上面的代碼獨立作為一個模塊,把結果保存在實例屬性self.res中,並創建一個新的方法getResult()來獲取這個值

import threading
from time import strftime

class MyThread(threading.Thread):
    def __init__(self,func,args,name=‘‘):
        threading.Thread.__init__(self)
        self.name = name
        self.func = func
        self.args = args
        
    def getResult(self):
        return self.res
    
    def run(self):
        print ‘starting‘,self.name,‘at:‘,strftime(‘%Y-%m-%d %H:%M:%S‘)
        self.res = self.func(*self.args)
        print self.name,‘finished at:‘,strftime(‘%Y-%m-%d %H:%M:%S‘)   

  

參考資料《python核心編程》(第3版) 

 

Python學習心得(七) 深入理解threading多線程模塊