threading
— 基於執行緒的並行¶
原始碼: Lib/threading.py
此模組在較低級別的 _thread
模組之上構建了更高級別的執行緒介面。
在 3.7 版本中更改: 此模組以前是可選的,現在始終可用。
另請參閱
concurrent.futures.ThreadPoolExecutor
提供了一個更高級別的介面,可以將任務推送到後臺執行緒,而不會阻塞呼叫執行緒的執行,同時仍然能夠在需要時檢索其結果。
queue
提供了一個執行緒安全的介面,用於在執行的執行緒之間交換資料。
asyncio
提供了一種替代方法來實現任務級別的併發,而無需使用多個作業系統執行緒。
註解
在 Python 2.x 系列中,此模組包含一些方法和函式的 camelCase
名稱。這些名稱從 Python 3.10 開始已棄用,但為了與 Python 2.5 及更低版本相容,仍然受支援。
CPython 實現細節: 在 CPython 中,由於 全域性直譯器鎖,一次只能執行一個執行緒的 Python 程式碼(即使某些面向效能的庫可能會克服此限制)。如果你希望你的應用程式更好地利用多核計算機的計算資源,建議你使用 multiprocessing
或 concurrent.futures.ProcessPoolExecutor
。但是,如果你想同時執行多個 I/O 密集型任務,執行緒仍然是一個合適的模型。
可用性: 不是 WASI。
此模組在 WebAssembly 上不起作用或不可用。有關詳細資訊,請參閱 WebAssembly 平臺。
此模組定義了以下函式
- threading.active_count()¶
返回當前處於活動狀態的
Thread
物件數量。返回的計數等於enumerate()
返回的列表的長度。函式
activeCount
是此函式的已棄用別名。
- threading.current_thread()¶
返回當前
Thread
物件,該物件對應於呼叫者的控制執行緒。如果呼叫者的控制執行緒不是透過threading
模組建立的,則返回一個功能有限的虛擬執行緒物件。函式
currentThread
是此函式的已棄用別名。
- threading.excepthook(args, /)¶
處理由
Thread.run()
引發的未捕獲異常。args 引數具有以下屬性
exc_type: 異常型別。
exc_value: 異常值,可以是
None
。exc_traceback: 異常回溯,可以是
None
。thread: 引發異常的執行緒,可以是
None
。
如果 exc_type 是
SystemExit
,則會靜默忽略該異常。否則,異常將列印到sys.stderr
。如果此函式引發異常,則會呼叫
sys.excepthook()
來處理它。可以重寫
threading.excepthook()
以控制如何處理由Thread.run()
引發的未捕獲異常。使用自定義鉤子儲存 exc_value 可能會建立一個引用迴圈。當不再需要該異常時,應顯式清除它以打破引用迴圈。
如果將 thread 設定為正在被終結的物件,則使用自定義鉤子儲存 thread 可以使其復活。避免在自定義鉤子完成後儲存 thread,以避免復活物件。
另請參閱
sys.excepthook()
處理未捕獲的異常。在 3.8 版本中新增。
- threading.__excepthook__¶
儲存
threading.excepthook()
的原始值。儲存它是為了在它們被損壞或替代物件替換的情況下,可以恢復原始值。在 3.10 版本中新增。
- threading.get_ident()¶
返回當前執行緒的“執行緒識別符號”。這是一個非零整數。它的值沒有直接含義;它旨在用作一個魔術 cookie,例如,用於索引執行緒特定資料的字典。當執行緒退出並建立另一個執行緒時,執行緒識別符號可能會被回收。
在 3.3 版本中新增。
- threading.get_native_id()¶
返回核心分配給當前執行緒的本機整數執行緒 ID。這是一個非負整數。它的值可用於在系統範圍內唯一標識此特定執行緒(直到執行緒終止,之後該值可能會被作業系統回收)。
可用性: Windows, FreeBSD, Linux, macOS, OpenBSD, NetBSD, AIX, DragonFlyBSD, GNU/kFreeBSD。
在 3.8 版本中新增。
在 3.13 版本中更改: 添加了對 GNU/kFreeBSD 的支援。
- threading.enumerate()¶
返回當前處於活動狀態的所有
Thread
物件的列表。該列表包括守護執行緒和由current_thread()
建立的虛擬執行緒物件。它排除已終止的執行緒和尚未啟動的執行緒。但是,主執行緒始終是結果的一部分,即使在終止時也是如此。
- threading.settrace(func)¶
為從
threading
模組啟動的所有執行緒設定一個跟蹤函式。在每個執行緒的run()
方法被呼叫之前,func 將被傳遞給sys.settrace()
。
- threading.settrace_all_threads(func)¶
為從
threading
模組啟動的所有執行緒以及當前正在執行的所有 Python 執行緒設定一個跟蹤函式。在每個執行緒的
run()
方法被呼叫之前,func 將被傳遞給sys.settrace()
。3.12 版本中新增。
- threading.gettrace()¶
獲取由
settrace()
設定的跟蹤函式。在 3.10 版本中新增。
- threading.setprofile(func)¶
為從
threading
模組啟動的所有執行緒設定一個性能分析函式。在每個執行緒的run()
方法被呼叫之前,func 將被傳遞給sys.setprofile()
。
- threading.setprofile_all_threads(func)¶
為從
threading
模組啟動的所有執行緒以及當前正在執行的所有 Python 執行緒設定一個性能分析函式。在每個執行緒的
run()
方法被呼叫之前,func 將被傳遞給sys.setprofile()
。3.12 版本中新增。
- threading.getprofile()¶
獲取由
setprofile()
設定的效能分析函式。在 3.10 版本中新增。
- threading.stack_size([size])¶
返回建立新執行緒時使用的執行緒堆疊大小。可選的 size 引數指定隨後建立的執行緒要使用的堆疊大小,必須為 0(使用平臺或配置的預設值)或至少為 32,768(32 KiB)的正整數值。如果未指定 size,則使用 0。如果不支援更改執行緒堆疊大小,則會引發
RuntimeError
。如果指定的堆疊大小無效,則會引發ValueError
,並且堆疊大小保持不變。目前,32 KiB 是保證直譯器本身有足夠堆疊空間的最小支援堆疊大小值。請注意,某些平臺可能對堆疊大小的值有特定的限制,例如要求最小堆疊大小 > 32 KiB,或要求以系統記憶體頁大小的倍數進行分配 - 有關更多資訊,應參閱平臺文件(4 KiB 頁面很常見;在沒有更具體資訊的情況下,建議將堆疊大小設定為 4096 的倍數)。可用性:Windows,pthreads。
支援 POSIX 執行緒的 Unix 平臺。
此模組還定義以下常量
- threading.TIMEOUT_MAX¶
阻塞函式的 timeout 引數允許的最大值(
Lock.acquire()
,RLock.acquire()
,Condition.wait()
等)。指定大於此值的超時將引發OverflowError
。3.2 版本中新增。
此模組定義了許多類,這些類將在下面的章節中詳細介紹。
此模組的設計大致基於 Java 的執行緒模型。然而,在 Java 中鎖和條件變數是每個物件的基本行為,而在 Python 中它們是單獨的物件。Python 的 Thread
類支援 Java 的 Thread 類行為的一個子集;目前,沒有優先順序、沒有執行緒組,並且執行緒不能被銷燬、停止、掛起、恢復或中斷。Java 的 Thread 類的靜態方法(如果已實現)會對映到模組級函式。
下面描述的所有方法都是原子執行的。
執行緒本地資料¶
執行緒本地資料是其值特定於執行緒的資料。要管理執行緒本地資料,只需建立 local
(或子類) 的例項並在其上儲存屬性即可
mydata = threading.local()
mydata.x = 1
該例項的值對於不同的執行緒將是不同的。
- class threading.local¶
表示執行緒本地資料的類。
有關更多詳細資訊和大量示例,請參閱
_threading_local
模組的文件字串:Lib/_threading_local.py。
執行緒物件¶
Thread
類表示在單獨的控制執行緒中執行的活動。有兩種方法可以指定活動:將可呼叫物件傳遞給建構函式,或者在子類中覆蓋 run()
方法。在子類中不應覆蓋其他方法(建構函式除外)。換句話說,僅覆蓋此類的 __init__()
和 run()
方法。
建立執行緒物件後,必須透過呼叫執行緒的 start()
方法來啟動其活動。這將呼叫單獨控制執行緒中的 run()
方法。
一旦執行緒的活動開始,該執行緒就被認為是“活躍”的。當其 run()
方法終止時,無論是以正常方式還是引發未處理的異常,它都會停止活躍狀態。 is_alive()
方法用於測試執行緒是否處於活躍狀態。
其他執行緒可以呼叫某個執行緒的 join()
方法。這將阻塞呼叫執行緒,直到呼叫了 join()
方法的執行緒終止。
一個執行緒有一個名稱。該名稱可以傳遞給建構函式,並透過 name
屬性讀取或更改。
如果 run()
方法引發異常,則會呼叫 threading.excepthook()
來處理它。預設情況下, threading.excepthook()
會靜默地忽略 SystemExit
異常。
一個執行緒可以被標記為“守護執行緒”。此標誌的意義在於,當只剩下守護執行緒時,整個 Python 程式就會退出。初始值是從建立執行緒繼承的。該標誌可以透過 daemon
屬性或 *daemon* 建構函式引數來設定。
註解
守護執行緒在關閉時會被突然停止。它們的資源(如開啟的檔案、資料庫事務等)可能無法正確釋放。如果您希望執行緒優雅地停止,請將它們設為非守護執行緒,並使用適當的訊號機制,例如 Event
。
存在一個“主執行緒”物件;它對應於 Python 程式中的初始控制執行緒。它不是守護執行緒。
有可能建立“虛擬執行緒物件”。這些是對應於“外部執行緒”的執行緒物件,這些執行緒是在 threading 模組之外啟動的控制執行緒,例如直接從 C 程式碼啟動。虛擬執行緒物件的功能有限;它們始終被視為活躍和守護執行緒,並且無法被 join。它們永遠不會被刪除,因為無法檢測到外部執行緒的終止。
- class threading.Thread(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)¶
此建構函式應始終使用關鍵字引數呼叫。引數為:
group 應為
None
;保留用於將來實現ThreadGroup
類時的擴充套件。target 是
run()
方法要呼叫的可呼叫物件。預設為None
,表示不呼叫任何內容。name 是執行緒名稱。預設情況下,會構造一個唯一名稱,形式為“Thread-N”,其中 N 是一個小的十進位制數字,或者“Thread-N (target)”,其中“target”是
target.__name__
(如果指定了 target 引數)。args 是目標呼叫的引數列表或元組。預設為
()
。kwargs 是目標呼叫的關鍵字引數字典。預設為
{}
。如果不是
None
,則 daemon 會顯式設定執行緒是否為守護執行緒。如果為None
(預設值),則守護程序屬性會從當前執行緒繼承。如果子類重寫了建構函式,則必須確保在對執行緒執行任何其他操作之前呼叫基類建構函式 (
Thread.__init__()
)。在 3.3 版本中更改: 添加了 daemon 引數。
在 3.10 版本中更改: 如果省略了 name 引數,則使用 target 名稱。
- start()¶
啟動執行緒的活動。
每個執行緒物件最多隻能呼叫一次。它會安排在單獨的控制執行緒中呼叫物件的
run()
方法。如果在同一執行緒物件上多次呼叫此方法,則會引發
RuntimeError
。
- run()¶
表示執行緒活動的方法。
您可以在子類中重寫此方法。標準的
run()
方法會呼叫傳遞給物件建構函式的、作為 target 引數的可呼叫物件(如果有),並使用分別來自 args 和 kwargs 引數的位置引數和關鍵字引數。將列表或元組作為傳遞給
Thread
的 args 引數,可以達到相同的效果。示例
>>> from threading import Thread >>> t = Thread(target=print, args=[1]) >>> t.run() 1 >>> t = Thread(target=print, args=(1,)) >>> t.run() 1
- join(timeout=None)¶
等待執行緒終止。這將阻塞呼叫執行緒,直到呼叫了
join()
方法的執行緒終止(無論是正常終止還是透過未處理的異常),或者直到可選的超時發生。當存在 timeout 引數且不為
None
時,它應為浮點數,指定操作的超時時間(以秒為單位或以秒的分數表示)。由於join()
始終返回None
,因此您必須在join()
之後呼叫is_alive()
,以確定是否發生了超時 – 如果執行緒仍然活躍,則join()
呼叫已超時。當 timeout 引數不存在或為
None
時,該操作將阻塞,直到執行緒終止。一個執行緒可以被多次 join。
如果嘗試加入當前執行緒(因為這會導致死鎖),則
join()
會引發RuntimeError
。線上程啟動之前join()
執行緒也是一個錯誤,嘗試這樣做會引發相同的異常。
- name¶
一個僅用於標識目的的字串。它沒有語義。多個執行緒可以具有相同的名稱。初始名稱由建構函式設定。
- ident¶
此執行緒的“執行緒識別符號”,如果執行緒尚未啟動,則為
None
。這是一個非零整數。請參閱get_ident()
函式。當一個執行緒退出並且建立另一個執行緒時,執行緒識別符號可能會被回收。即使執行緒已退出,該識別符號仍然可用。
- native_id¶
此執行緒的執行緒 ID (
TID
),由作業系統(核心)分配。這是一個非負整數,如果執行緒尚未啟動,則為None
。請參閱get_native_id()
函式。此值可用於在整個系統中唯一標識此特定執行緒(直到執行緒終止,之後該值可能被作業系統回收)。註解
類似於程序 ID,執行緒 ID 僅線上程建立到執行緒終止這段時間內有效(保證在整個系統中是唯一的)。
可用性:Windows、FreeBSD、Linux、macOS、OpenBSD、NetBSD、AIX、DragonFlyBSD。
在 3.8 版本中新增。
- is_alive()¶
返回執行緒是否處於活動狀態。
此方法在
run()
方法開始之前,直到run()
方法終止之後,返回True
。模組函式enumerate()
返回所有活動執行緒的列表。
- daemon¶
一個布林值,指示此執行緒是否為守護執行緒 (
True
) 或不是 (False
)。必須在呼叫start()
之前設定此值,否則會引發RuntimeError
。它的初始值繼承自建立執行緒;主執行緒不是守護執行緒,因此在主執行緒中建立的所有執行緒預設daemon
=False
。當沒有活動的非守護執行緒時,整個 Python 程式會退出。
鎖物件¶
原始鎖是一種同步原語,在被鎖定時不歸特定執行緒所有。在 Python 中,它目前是可用的最低級別的同步原語,由 _thread
擴充套件模組直接實現。
原始鎖有兩種狀態,“鎖定”或“未鎖定”。它在未鎖定狀態下建立。它有兩個基本方法,acquire()
和 release()
。當狀態為未鎖定時,acquire()
將狀態更改為鎖定並立即返回。當狀態為鎖定時,acquire()
會阻塞,直到另一個執行緒呼叫 release()
將其更改為未鎖定,然後 acquire()
呼叫將其重置為鎖定並返回。release()
方法只能在鎖定狀態下呼叫;它將狀態更改為未鎖定並立即返回。如果嘗試釋放一個未鎖定的鎖,則會引發 RuntimeError
。
鎖還支援 上下文管理協議。
當多個執行緒在 acquire()
中阻塞,等待狀態變為未鎖定時,當 release()
呼叫將狀態重置為未鎖定時,只有一個執行緒會繼續執行;哪個等待執行緒會繼續執行是不確定的,並且可能因不同的實現而異。
所有方法都是原子執行的。
- class threading.Lock¶
實現原始鎖物件的類。一旦執行緒獲取了鎖,後續獲取鎖的嘗試都會被阻塞,直到鎖被釋放;任何執行緒都可以釋放鎖。
在 3.13 版本中更改:
Lock
現在是一個類。在早期的 Python 中,Lock
是一個工廠函式,它返回底層私有鎖型別的例項。- acquire(blocking=True, timeout=-1)¶
獲取鎖,阻塞或非阻塞。
當使用設定為
True
(預設值) 的 *blocking* 引數呼叫時,會阻塞直到鎖被解鎖,然後將其設定為鎖定並返回True
。當使用設定為
False
的 *blocking* 引數呼叫時,不會阻塞。如果設定為True
的 *blocking* 呼叫會阻塞,則立即返回False
;否則,將鎖設定為鎖定並返回True
。當使用設定為正值的浮點數 *timeout* 引數呼叫時,最多會阻塞 *timeout* 指定的秒數,只要無法獲取鎖。 *timeout* 引數為
-1
指定無限等待。當 *blocking* 為False
時,禁止指定 *timeout*。如果成功獲取鎖,則返回值為
True
,否則返回False
(例如,如果 *timeout* 過期)。在 3.2 版本中更改: *timeout* 引數是新增的。
在 3.2 版本中更改: 如果底層執行緒實現支援,則在 POSIX 上獲取鎖現在可以被訊號中斷。
- release()¶
釋放鎖。這可以從任何執行緒呼叫,而不僅僅是獲取鎖的執行緒。
當鎖被鎖定時,將其重置為未鎖定,然後返回。如果任何其他執行緒被阻塞等待鎖變為未鎖定狀態,則允許其中一個執行緒繼續執行。
當在未鎖定的鎖上呼叫時,會引發
RuntimeError
。沒有返回值。
- locked()¶
如果鎖被獲取,則返回
True
。
RLock 物件¶
可重入鎖是一種同步原語,同一執行緒可以多次獲取它。在內部,除了原始鎖使用的鎖定/未鎖定狀態之外,它還使用“擁有執行緒”和“遞迴級別”的概念。在鎖定狀態下,某些執行緒擁有鎖;在未鎖定狀態下,沒有執行緒擁有鎖。
執行緒呼叫鎖的 acquire()
方法來鎖定它,並呼叫其 release()
方法來解鎖它。
與 Lock 的 acquire()
/release()
呼叫對不同,RLock 的 acquire()
/release()
呼叫對可以巢狀。只有最終的 release()
(最外層配對的 release()
)才會將鎖重置為未鎖定狀態,並允許另一個在 acquire()
中阻塞的執行緒繼續執行。
acquire()
/release()
必須成對使用:每次 acquire 都必須在獲取鎖的執行緒中有一個 release。未能釋放與獲取鎖次數相同的次數可能會導致死鎖。
- class threading.RLock¶
此類實現可重入鎖物件。可重入鎖必須由獲取它的執行緒釋放。一旦執行緒獲取了可重入鎖,同一個執行緒可以再次獲取它而不會阻塞;執行緒必須在每次獲取後釋放一次。
請注意,
RLock
實際上是一個工廠函式,它返回平臺支援的具體 RLock 類中最有效版本的例項。- acquire(blocking=True, timeout=-1)¶
獲取鎖,阻塞或非阻塞。
另請參閱
- 將 RLock 用作上下文管理器
在實際可行的情況下,建議使用上下文管理器而不是手動呼叫
acquire()
和release()
。
當呼叫時,blocking 引數設定為
True
(預設值)當呼叫時,blocking 引數設定為
False
如果沒有執行緒擁有該鎖,則獲取該鎖並立即返回。
如果另一個執行緒擁有該鎖,則立即返回。
如果同一執行緒擁有該鎖,則再次獲取該鎖並立即返回。
在所有情況下,如果執行緒能夠獲取鎖,則返回
True
。如果執行緒無法獲取鎖(即,如果不阻塞或達到超時時間),則返回False
。如果多次呼叫,未能呼叫與次數相同的
release()
可能會導致死鎖。考慮使用RLock
作為上下文管理器,而不是直接呼叫 acquire/release。在 3.2 版本中更改: *timeout* 引數是新增的。
- release()¶
釋放鎖,遞減遞迴級別。如果在遞減後為零,則將鎖重置為未鎖定狀態(不被任何執行緒擁有),並且如果有任何其他執行緒被阻塞等待鎖變為未鎖定狀態,則允許其中一個執行緒繼續執行。如果在遞減後遞迴級別仍然非零,則鎖保持鎖定狀態並由呼叫執行緒擁有。
僅當呼叫執行緒擁有該鎖時才呼叫此方法。如果鎖未被獲取時呼叫此方法,則會引發
RuntimeError
。沒有返回值。
條件物件¶
條件變數始終與某種鎖相關聯;可以傳入鎖,或者預設情況下會建立一個鎖。當多個條件變數必須共享同一個鎖時,傳入鎖很有用。鎖是條件物件的一部分:您不必單獨跟蹤它。
條件變數遵循上下文管理協議:在封閉塊的持續時間內,使用 with
語句會獲取關聯的鎖。 acquire()
和 release()
方法還會呼叫關聯鎖的相應方法。
必須在持有關聯鎖的情況下呼叫其他方法。wait()
方法會釋放鎖,然後阻塞直到另一個執行緒透過呼叫 notify()
或 notify_all()
來喚醒它。喚醒後, wait()
會重新獲取鎖並返回。也可以指定超時時間。
如果存在等待條件變數的執行緒, notify()
方法會喚醒其中一個執行緒。 notify_all()
方法會喚醒所有等待條件變數的執行緒。
注意: notify()
和 notify_all()
方法不會釋放鎖;這意味著被喚醒的執行緒不會立即從它們的 wait()
呼叫中返回,而只有在呼叫 notify()
或 notify_all()
的執行緒最終放棄對鎖的所有權時才會返回。
使用條件變數的典型程式設計風格是使用鎖來同步對某些共享狀態的訪問;對特定狀態更改感興趣的執行緒會重複呼叫 wait()
,直到它們看到所需的狀態,而修改狀態的執行緒會在以某種方式更改狀態時呼叫 notify()
或 notify_all()
,使其可能成為其中一個等待者的期望狀態。例如,以下程式碼是具有無限緩衝區容量的通用生產者-消費者情況
# Consume one item
with cv:
while not an_item_is_available():
cv.wait()
get_an_available_item()
# Produce one item
with cv:
make_an_item_available()
cv.notify()
檢查應用程式條件的 while
迴圈是必要的,因為 wait()
可以在任意長的時間後返回,並且導致 notify()
呼叫的條件可能不再成立。這是多執行緒程式設計固有的。wait_for()
方法可用於自動執行條件檢查,並簡化超時的計算。
# Consume an item
with cv:
cv.wait_for(an_item_is_available)
get_an_available_item()
要在 notify()
和 notify_all()
之間進行選擇,請考慮一個狀態更改是否僅對一個或多個等待執行緒感興趣。例如,在典型的生產者-消費者情況下,向緩衝區新增一個專案只需要喚醒一個消費者執行緒。
- class threading.Condition(lock=None)¶
此類實現條件變數物件。條件變數允許一個或多個執行緒等待,直到它們被另一個執行緒通知。
如果提供了 lock 引數且不為
None
,則它必須是一個Lock
或RLock
物件,並將其用作底層鎖。否則,將建立一個新的RLock
物件並將其用作底層鎖。在 3.3 版本中變更: 從工廠函式改為類。
- acquire(*args)¶
獲取底層鎖。此方法呼叫底層鎖的相應方法;返回值是該方法返回的任何值。
- release()¶
釋放底層鎖。此方法呼叫底層鎖的相應方法;沒有返回值。
- wait(timeout=None)¶
等待直到被通知或發生超時。如果呼叫此方法時呼叫執行緒未獲取鎖,則會引發
RuntimeError
。此方法釋放底層鎖,然後阻塞,直到它被另一個執行緒中同一條件變數的
notify()
或notify_all()
呼叫喚醒,或直到可選的超時發生。一旦被喚醒或超時,它將重新獲取鎖並返回。當 timeout 引數存在且不為
None
時,它應該是一個浮點數,指定操作的超時時間(以秒為單位,或秒的分數)。當底層鎖是
RLock
時,它不會使用其release()
方法釋放,因為當它被遞迴多次獲取時,這可能實際上不會解鎖。相反,將使用RLock
類的內部介面,即使它已被遞迴獲取多次,也真正解鎖它。然後使用另一個內部介面在重新獲取鎖時恢復遞迴級別。返回值是
True
,除非給定的 timeout 過期,在這種情況下它是False
。在 3.2 版本中變更: 之前,該方法總是返回
None
。
- wait_for(predicate, timeout=None)¶
等待直到條件求值為真。predicate 應該是一個可呼叫物件,其結果將被解釋為布林值。可以提供一個 timeout 來指定最大等待時間。
此實用方法可以重複呼叫
wait()
,直到謂詞滿足或發生超時。返回值是謂詞的最後返回值,如果方法超時,則將求值為False
。忽略超時功能,呼叫此方法大致等同於編寫
while not predicate(): cv.wait()
因此,與
wait()
相同,適用相同的規則:呼叫時必須持有鎖,並在返回時重新獲取鎖。謂詞是在持有鎖的情況下求值的。3.2 版本中新增。
- notify(n=1)¶
預設情況下,喚醒一個正在等待此條件的執行緒(如果有)。如果呼叫此方法時呼叫執行緒未獲取鎖,則會引發
RuntimeError
。此方法最多喚醒 n 個正在等待條件變數的執行緒;如果沒有執行緒在等待,則這是一個空操作。
當前的實現會準確地喚醒 n 個執行緒,如果至少有 n 個執行緒在等待。但是,依賴此行為是不安全的。未來,最佳化的實現可能會偶爾喚醒超過 n 個執行緒。
注意:喚醒的執行緒實際上不會從其
wait()
呼叫返回,直到它可以重新獲取鎖。由於notify()
不會釋放鎖,因此其呼叫者應該釋放鎖。
- notify_all()¶
喚醒所有正在等待此條件的執行緒。此方法的行為類似於
notify()
,但會喚醒所有等待的執行緒,而不是一個。如果呼叫此方法時呼叫執行緒未獲取鎖,則會引發RuntimeError
。方法
notifyAll
是此方法的已棄用別名。
訊號量物件¶
這是計算機科學史上最古老的同步原語之一,由早期的荷蘭計算機科學家 Edsger W. Dijkstra 發明(他使用名稱 P()
和 V()
而不是 acquire()
和 release()
)。
訊號量管理一個內部計數器,該計數器透過每次 acquire()
呼叫遞減,並透過每次 release()
呼叫遞增。計數器永遠不會低於零;當 acquire()
發現它為零時,它會阻塞,等待直到其他執行緒呼叫 release()
。
訊號量還支援 上下文管理協議。
- class threading.Semaphore(value=1)¶
此類實現訊號量物件。訊號量管理一個原子計數器,表示
release()
呼叫的次數減去acquire()
呼叫的次數,再加上初始值。acquire()
方法在必要時會阻塞,直到它可以返回而不使計數器變為負數。如果未給定,則 value 預設為 1。可選引數給出內部計數器的初始 value;它預設為
1
。如果給定的 value 小於 0,則會引發ValueError
。在 3.3 版本中變更: 從工廠函式改為類。
- acquire(blocking=True, timeout=None)¶
獲取訊號量。
當不帶引數呼叫時
如果內部計數器在進入時大於零,則將其減一併立即返回
True
。如果內部計數器在進入時為零,則阻塞直到被對
release()
的呼叫喚醒。一旦被喚醒(並且計數器大於 0),則將計數器減 1 並返回True
。每次呼叫release()
都會喚醒一個執行緒。不應依賴喚醒執行緒的順序。
當使用設定為
False
的 blocking 呼叫時,不阻塞。如果一個不帶引數的呼叫會阻塞,則立即返回False
;否則,執行與不帶引數呼叫時相同的操作,並返回True
。當使用除
None
之外的 timeout 引數呼叫時,它將最多阻塞 timeout 秒。如果在此時間間隔內獲取操作未成功完成,則返回False
。否則返回True
。在 3.2 版本中更改: *timeout* 引數是新增的。
- release(n=1)¶
釋放一個訊號量,將內部計數器增加 n。當它在進入時為零,並且其他執行緒正在等待它再次大於零時,喚醒其中的 n 個執行緒。
3.9 版本更改: 添加了 n 引數,以便一次釋放多個等待執行緒。
- class threading.BoundedSemaphore(value=1)¶
實現有界訊號量物件的類。有界訊號量會檢查其當前值是否超過其初始值。如果超過,則會引發
ValueError
異常。在大多數情況下,訊號量用於保護容量有限的資源。如果訊號量釋放次數過多,則表明存在錯誤。如果未給定 value,則預設為 1。在 3.3 版本中變更: 從工廠函式改為類。
Semaphore
示例¶
訊號量通常用於保護容量有限的資源,例如資料庫伺服器。在任何資源大小固定的情況下,都應使用有界訊號量。在生成任何工作執行緒之前,主執行緒會初始化訊號量
maxconnections = 5
# ...
pool_sema = BoundedSemaphore(value=maxconnections)
一旦生成,工作執行緒會在需要連線到伺服器時呼叫訊號量的 acquire 和 release 方法
with pool_sema:
conn = connectdb()
try:
# ... use connection ...
finally:
conn.close()
使用有界訊號量可以減少因程式設計錯誤導致訊號量釋放次數多於獲取次數而被忽略的可能性。
事件物件¶
這是執行緒之間進行通訊的最簡單機制之一:一個執行緒發出事件訊號,其他執行緒等待該事件。
事件物件管理一個內部標誌,可以使用 set()
方法將其設定為 true,並使用 clear()
方法將其重置為 false。 wait()
方法會阻塞,直到標誌為 true。
- class threading.Event¶
實現事件物件的類。事件管理一個標誌,可以使用
set()
方法將其設定為 true,並使用clear()
方法將其重置為 false。wait()
方法會阻塞,直到標誌為 true。該標誌的初始值為 false。在 3.3 版本中變更: 從工廠函式改為類。
- is_set()¶
當且僅當內部標誌為 true 時,返回
True
。方法
isSet
是此方法的已棄用別名。
- wait(timeout=None)¶
只要內部標誌為 false 且未超出給定的超時時間(如果存在),則一直阻塞。返回值表示此阻塞方法返回的原因;如果返回原因是內部標誌被設定為 true,則返回
True
;如果給出了超時時間並且在給定的等待時間內內部標誌未變為 true,則返回False
。當 timeout 引數存在且不為
None
時,它應為浮點數,用於指定操作的超時時間(以秒為單位,或幾分之一秒)。3.1 版本更改: 以前,該方法始終返回
None
。
定時器物件¶
此類表示僅在經過一定時間後才應執行的操作 — 定時器。 Timer
是 Thread
的子類,因此也可以作為建立自定義執行緒的示例。
與執行緒一樣,透過呼叫其 Timer.start
方法來啟動定時器。可以透過呼叫 cancel()
方法來停止定時器(在其操作開始之前)。定時器在執行其操作之前等待的時間間隔可能與使用者指定的時間間隔不完全相同。
例如
def hello():
print("hello, world")
t = Timer(30.0, hello)
t.start() # after 30 seconds, "hello, world" will be printed
屏障物件¶
3.2 版本中新增。
此類為需要相互等待的固定數量的執行緒提供了一個簡單的同步原語。每個執行緒都嘗試透過呼叫 wait()
方法來透過屏障,並且會阻塞,直到所有執行緒都進行了 wait()
呼叫。此時,執行緒同時釋放。
屏障可以為相同數量的執行緒重複使用任意次數。
例如,以下是一種簡單的同步客戶端和伺服器執行緒的方法
b = Barrier(2, timeout=5)
def server():
start_server()
b.wait()
while True:
connection = accept_connection()
process_server_connection(connection)
def client():
b.wait()
while True:
connection = make_connection()
process_client_connection(connection)
- class threading.Barrier(parties, action=None, timeout=None)¶
為 parties 數量的執行緒建立一個屏障物件。action,如果提供,是一個可呼叫物件,當執行緒被釋放時,由其中一個執行緒呼叫。timeout 是預設的超時值,如果
wait()
方法沒有指定超時值,則使用此值。- wait(timeout=None)¶
透過屏障。當所有參與屏障的執行緒都呼叫此函式時,它們將同時被釋放。如果提供了 timeout,則優先使用它,而不是類建構函式中提供的任何超時值。
返回值是一個介於 0 到 parties - 1 的整數,每個執行緒都不同。這可以用來選擇一個執行緒來執行一些特殊的維護操作,例如:
i = barrier.wait() if i == 0: # Only one thread needs to print this print("passed the barrier")
如果為建構函式提供了 action,則其中一個執行緒將在釋放之前呼叫它。如果此呼叫引發錯誤,則屏障將進入損壞狀態。
如果呼叫超時,則屏障將進入損壞狀態。
如果屏障線上程等待時被破壞或重置,則此方法可能會引發
BrokenBarrierError
異常。
- reset()¶
將屏障恢復到預設的空狀態。任何等待它的執行緒都將收到
BrokenBarrierError
異常。請注意,如果存在狀態未知的其他執行緒,則使用此函式可能需要一些外部同步。如果屏障損壞,最好只是保留它並建立一個新的屏障。
- abort()¶
將屏障置於損壞狀態。這會導致任何活動的或未來的
wait()
呼叫失敗,並出現BrokenBarrierError
。例如,如果其中一個執行緒需要中止,則可以使用此方法來避免應用程式死鎖。最好使用合理的 timeout 值建立屏障,以自動防止其中一個執行緒出錯。
- parties¶
透過屏障所需的執行緒數。
- n_waiting¶
當前正在屏障中等待的執行緒數。
- broken¶
一個布林值,如果屏障處於損壞狀態,則為
True
。
- exception threading.BrokenBarrierError¶
當
Barrier
物件被重置或損壞時,會引發此異常,它是RuntimeError
的子類。
在 with
語句中使用鎖、條件和訊號量¶
此模組提供的所有具有 acquire
和 release
方法的物件都可以用作 with
語句的上下文管理器。當進入塊時將呼叫 acquire
方法,當退出塊時將呼叫 release
方法。因此,以下程式碼片段
with some_lock:
# do something...
等效於
some_lock.acquire()
try:
# do something...
finally:
some_lock.release()
目前,Lock
、RLock
、Condition
、Semaphore
和 BoundedSemaphore
物件可以用作 with
語句的上下文管理器。