參考書籍:python核心編程
_thread模塊除了可以派生線程外,還提供了基本的同步數據結構,又稱為鎖對象(lock object,也叫原語鎖、簡單鎖、互斥鎖、互斥和二進制信號量)。
下面是常用的線程函數:
函數 | 描述 |
start_new_thread(function,args,kwargs=None) | 派生一個新的線程,使用給定的args和可選的kwargs來執行function |
allocate_lock() | 分配LockType對象 |
exit() | 退出線程指令 |
LockType鎖對象的方法 | |
acquire(wait=None) | 嘗試獲取鎖對象 |
locked() | 如果獲取了鎖對象則返回True,否則返回False |
release() | 釋放鎖 |
_thread模塊的核心函數是start_new_thread()。專門用來派生新的線程。
我們對上節文章的onethr.py文件稍作修改:
#!/usr/bin/env python import _thread from time import sleep,ctime def loop0(): print('開始循環0次在:',ctime()) sleep(4) print('結束循環0次在:',ctime()) def loop1(): print('開始循環1次在:',ctime()) sleep(2) print('結束循環1次在:',ctime()) ''' def main(): print('開始於:',ctime()) loop0() loop1() print('所有的任務都完成於:',ctime()) ''' def main(): print('starting at:', ctime()) _thread.start_new_thread(loop0, ()) _thread.start_new_thread(loop1, ()) sleep(6) print('all done at:', ctime()) if __name__ =='__main__': main(
執行該腳本三遍,結果:
PS C:\Users\WC> python E:\Python3.6.3\workspace\mtsleepA.py starting at: Mon Mar 26 21:56:10 2018 開始循環1次在: Mon Mar 26 21:56:10 2018 開始循環0次在: Mon Mar 26 21:56:10 2018 結束循環1次在: Mon Mar 26 21:56:12 2018 結束循環0次在: Mon Mar 26 21:56:14 2018 all done at: Mon Mar 26 21:56:16 2018
PS C:\Users\WC> python E:\Python3.6.3\workspace\mtsleepA.py
starting at: Mon Mar 26 22:00:43 2018
開始循環0次在: Mon Mar 26 22:00:43 2018
開始循環1次在: Mon Mar 26 22:00:43 2018
結束循環1次在: Mon Mar 26 22:00:45 2018
結束循環0次在: Mon Mar 26 22:00:47 2018
all done at: Mon Mar 26 22:00:49 2018
PS C:\Users\WC> python E:\Python3.6.3\workspace\mtsleepA.py
starting at: Mon Mar 26 22:00:56 2018
開始循環0次在: Mon Mar 26 22:00:56 2018
開始循環1次在: Mon Mar 26 22:00:56 2018
結束循環1次在: Mon Mar 26 22:00:58 2018
結束循環0次在: Mon Mar 26 22:01:00 2018
all done at: Mon Mar 26 22:01:02 2018
由上面的代碼可知,start_new_thread()必須包含兩個參數,即使要執行的函數不需要參數,也要傳遞一個空元組。
我們注意到:loop0還是loop1開始的順序竟然可以是無序的;loop0和loop1是同時執行的;loop1是在loop0之前結束的;整個程序一共耗時6秒。
我們可以說,loop0和loop1是並發執行的。
我們在主程序(其實也就是主線程)中增加了一個sleep(6)的語句,這其實是為了避免主程序結束的時候,loop0和loop1兩個線程還沒有結束的問題。這也是_thread模塊的一種線程同步機制。
但是,我們要說這樣使用sleep()來進行線程同步是不靠譜的,這也是_thread的一個弊端所在。
這時,我們可以引用鎖機制來實現相應的線程管理,並且同時改善單獨的循環函數實現方式:
import _thread from time import sleep, ctime #不再把4秒和2秒硬性的編碼到不同的函數中,而是使用唯一的loop()函數,並把這些常量放進列表loops中 loops=[4,2] #代替了之前的loop*()函數,三個參數分別代表了處於第幾個循環中,睡眠時間和鎖對象。每個循環執行到最后一句的時候,釋放鎖對象,告訴主線程該線程已完成 def loop(nloop,sec,lock): print('開始循環',nloop,'在:',ctime()) sleep(sec) print('循環',nloop ,'結束於:',ctime()) lock.release() def main(): print('開始於:',ctime()) locks=[] nloops=range(len(loops)) #第一個for循環中,創建了一個鎖的列表,通過thread.allocate_lock()方法得到鎖對象,再通過acquire()方法取到鎖(相當於把鎖鎖上),取到之后就可以把它添加到鎖列表locks中。 for i in nloops: lock=_thread.allocate_lock() lock.acquire() locks.append(lock) #第二個for循環中,主要用於派生線程。每個線程都會調用loop()函數,並傳遞循環號、睡眠時間以及用於該線程的鎖。 for i in nloops: _thread.start_new_thread(loop,(i,loops[i],locks[i])) #第三個for循環,按照順序檢查每個鎖。每個線程執行完畢后,都會釋放自己的鎖對象。這里使用忙等待,讓主線程等所有的鎖都釋放后才繼續執行 for i in nloops: while locks[i].locked(): pass print('所有的任務完成於:',ctime()) if __name__ =='__main__': main()
執行結果:
開始循環 1 在: Mon Mar 26 22:49:25 2018 開始循環 0 在: Mon Mar 26 22:49:25 2018 循環 1 結束於: Mon Mar 26 22:49:27 2018 循環 0 結束於: Mon Mar 26 22:49:29 2018 所有的任務完成於: Mon Mar 26 22:49:29 2018
上述結果除了表名兩次循環是並發執行的之外,整個程序一共用時4秒,而不是之前的6秒。