1. 程式人生 > 程式設計 >python多執行緒使用方法例項詳解

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程式設計有所幫助。