python多執行緒使用方法例項詳解
本文例項講述了python多執行緒使用方法。分享給大家供大家參考,具體如下:
threading 模組支援守護執行緒, 其工作方式是:守護執行緒一般是一個等待客戶端請求服務的伺服器。
如果把一個執行緒設定為守護執行緒,程序退出時不需要等待這個執行緒執行完成。
如果主執行緒準備退出時,不需要等待某些子執行緒完成,就可以為這些子執行緒設定守護執行緒標記。 需要在啟動執行緒之前執行如下賦值語句: thread.daemon = True,檢查執行緒的守護狀態也只需要檢查這個值即可。
整個 Python 程式將在所有非守護執行緒退出之後才退出, 換句話說, 就是沒有剩下存活的非守護執行緒時才退出。
使用thread模組
以下是三種使用 Thread 類的方法(一般使用第一個或第三個方案)
- 建立 Thread 的例項,傳給它一個函式。
import threading from time import sleep,ctime loops = [3,2,1,1] def loop(i,nsec): print(f'start loop {i} at: {ctime()}') sleep(nsec) print(f'end loop {i} at: {ctime()}') def main(): print('start at',ctime()) threads = [] nloops = range(len(loops)) for i in nloops: t = threading.Thread(target=loop,args=(i,loops[i])) threads.append(t) for i in nloops: # start threads threads[i].start() for i in nloops: # wait for all threads[i].join() # threads to finish print(f'all done at: {ctime()}') if __name__ == '__main__': main()
當所有執行緒都分配完成之後,通過呼叫每個執行緒的 start()方法讓它們開始執行,而不是 在這之前就會執行。
相比於管理一組鎖(分配、獲取、釋放、檢查鎖狀態等)而言,這裡只 需要為每個執行緒呼叫 join()方法即可。
join()方法將等待執行緒結束,或者在提供了超時時間的情況下,達到超時時間。
使用 join()方法要比等待鎖釋放的無限迴圈更加清晰(這也是這種鎖 又稱為自旋鎖的原因)。
- 建立 Thread 的例項,傳給它一個可呼叫的類例項。
import threading from time import sleep,ctime # 建立 Thread 的例項,傳給它一個可呼叫的類例項 loops = [3,1] class ThreadFunc(object): def __init__(self,func,args,name=''): self.name = name self.func = func self.args = args def __call__(self): self.func(*self.args) def loop(i,ctime()) threads = [] nloops = range(len(loops)) for i in nloops: t = threading.Thread(target=ThreadFunc(loop,(i,loops[i]),loop.__name__)) threads.append(t) for i in nloops: # start threads threads[i].start() for i in nloops: # wait for all threads[i].join() # threads to finish print(f'all done at: {ctime()}') if __name__ == '__main__': main()
- 派生 Thread 的子類,並建立子類的例項。
import threading from time import sleep,ctime # 建立 Thread 的例項,傳給它一個可呼叫的類例項 # 子類的建構函式必須先呼叫其基類的建構函式 # 特殊方法__call__()在 子類中必須要寫為 run() loops = [3,1] class MyThread(threading.Thread): def __init__(self,name=''): threading.Thread.__init__(self) self.name = name self.func = func self.args = args def run(self): self.func(*self.args) def loop(i,ctime()) threads = [] nloops = range(len(loops)) for i in nloops: t = MyThread(loop,loop.__name__) threads.append(t) for i in nloops: # start threads threads[i].start() for i in nloops: # wait for all threads[i].join() # threads to finish print(f'all done at: {ctime()}') if __name__ == '__main__': main()
使用鎖
python和java一樣,也具有鎖機制,而且建立與使用鎖都是很簡便的。
一般在多執行緒程式碼中,總會有一些特 定的函式或程式碼塊不希望(或不應該)被多個執行緒同時執行,通常包括修改資料庫、更新檔案或 其他會產生競態條件的類似情況
鎖有兩種狀態:鎖定和未鎖定。而且它也只支援兩個函式:獲得鎖和釋放鎖。
一般鎖的呼叫如下
# 載入執行緒的鎖物件 lock = threading.Lock() # 獲取鎖 lock.acquire() # ...程式碼 # 釋放鎖 lock.release()
更簡潔的方法是使用with關鍵字,如下程式碼功能同上
# 載入執行緒的鎖物件 lock = threading.Lock() with lock : #...程式碼
示例程式碼:
import threading from time import sleep,ctime lock = threading.Lock() def a(): lock.acquire() for x in range(5): print(f'a:{str(x)}') sleep(0.01) lock.release() def b(): lock.acquire() for x in range(5): print(f'a:{str(x)}') sleep(0.01) lock.release() threading.Thread(target=a).start() threading.Thread(target=b).start()
相關屬性和方法
- Thread物件的屬性
屬性 | 描述 |
---|---|
name | 執行緒名 |
ident | 執行緒的識別符號 |
daemon | 布林標誌,表示這個執行緒是否是守護執行緒 |
- Thread物件的方法
方法 | 描述 |
---|---|
init(group=None,tatget=None,name=None,args=(),kwargs ={},verbose=None,daemon=None) | 例項化一個執行緒物件,需要有一個可呼叫的 target,以及其引數 args 或 kwargs。還可以傳遞 name 或 group 引數,不過後者還未實現。此 外 , verbose 標 志 也 是 可 接 受 的 。 而 daemon 的 值 將 會 設 定 thread.daemon 屬性/標誌 |
start() | 開始執行該執行緒 |
run() | 定義執行緒功能的方法(通常在子類中被應用開發者重寫) |
join (timeout=None) | 直至啟動的執行緒終止之前一直掛起;除非給出了 timeout(秒),否則 會一直阻塞 |
is_alive() | 布林標誌,表示這個執行緒是否還存活 |
- threading模組其他函式
函式 | 描述 |
---|---|
start() | 開始執行該執行緒 |
active_count() | 當前活動的 Thread 物件個數 |
enumerate() | 返回當前活動的 Thread 物件列表 |
settrace(func) | 為所有執行緒設定一個 trace 函式 |
setprofile (func) | 為所有執行緒設定一個 profile 函式 |
stack_size(size=0) | 返回新建立執行緒的棧大小;或為後續建立的執行緒設定棧的大小 為 size |
Lock() | 載入執行緒的鎖物件,是一個基本的鎖物件,一次只能一個鎖定,其餘鎖請求,需等待鎖釋放後才能獲取,物件有acquire()和release()方法 |
RLock() | 多重鎖,在同一執行緒中可用被多次acquire。如果使用RLock,那麼acquire和release必須成對出現,呼叫了n次acquire鎖請求,則必須呼叫n次的release才能線上程中釋放鎖物件 |
後記
在Python多執行緒下,每個執行緒的執行方式:
1、獲取GIL
2、執行程式碼直到sleep或者是python虛擬機器將其掛起。
3、釋放GIL
通常來說,多執行緒是一個好東西。不過由於 Python 的 GIL 的限制,多執行緒更適合於 I/O 密集型應用(I/O 釋放了 GIL,可以允 許更多的併發),而不是計算密集型應用。對於後一種情況而言,為了實現更好的並行性,你需要使用多程序,以便讓 CPU 的其他核心來執行。
請注意:多核多執行緒比單核多執行緒更差,原因是單核下多執行緒,每次釋放GIL,喚醒的那個執行緒都能獲取到GIL鎖,所以能夠無縫執行,但多核下,CPU0釋放GIL後,其他CPU上的執行緒都會進行競爭,但GIL可能會馬上又被CPU0拿到,導致其他幾個CPU上被喚醒後的執行緒會醒著等待到切換時間後又進入待排程狀態,這樣會造成執行緒顛簸(thrashing),導致效率更低
更多關於Python相關內容感興趣的讀者可檢視本站專題:《Python程序與執行緒操作技巧總結》、《Python資料結構與演算法教程》、《Python函式使用技巧總結》、《Python字串操作技巧彙總》、《Python入門與進階經典教程》、《Python+MySQL資料庫程式設計入門教程》及《Python常見資料庫操作技巧彙總》
希望本文所述對大家Python程式設計有所幫助。