threading --- 基于線程的并行?

源代碼: Lib/threading.py


這個模塊在較低級的模塊 _thread 基礎上建立較高級的線程接口。參見: queue 模塊。

在 3.7 版更改: 這個模塊曾經(jīng)為可選項,但現(xiàn)在總是可用。

備注

在 Python 2.x 系列中,此模塊包含有某些方法和函數(shù) camelCase 形式的名稱。 它們在 Python 3.10 中已棄用,但為了與 Python 2.5 及更舊版本的兼容性而仍受到支持。

CPython implementation detail: 在 CPython 中,由于存在 全局解釋器鎖,同一時刻只有一個線程可以執(zhí)行 Python 代碼(雖然某些性能導向的庫可能會去除此限制)。 如果你想讓你的應用更好地利用多核心計算機的計算資源,推薦你使用 multiprocessingconcurrent.futures.ProcessPoolExecutor。 但是,如果你想要同時運行多個 I/O 密集型任務,則多線程仍然是一個合適的模型。

這個模塊定義了以下函數(shù):

threading.active_count()?

返回當前存活的 Thread 對象的數(shù)量。 返回值與 enumerate() 所返回的列表長度一致。

函數(shù) activeCount 是此函數(shù)的已棄用別名。

threading.current_thread()?

返回當前對應調(diào)用者的控制線程的 Thread 對象。如果調(diào)用者的控制線程不是利用 threading 創(chuàng)建,會返回一個功能受限的虛擬線程對象。

函數(shù) currentThread 是此函數(shù)的已棄用別名。

threading.excepthook(args, /)?

處理由 Thread.run() 引發(fā)的未捕獲異常。

args 參數(shù)具有以下屬性:

  • exc_type: 異常類型

  • exc_value: 異常值,可以是 None.

  • exc_traceback: 異?;厮?,可以是 None.

  • thread: 引發(fā)異常的線程,可以為 None

如果 exc_typeSystemExit,則異常會被靜默地忽略。 在其他情況下,異常將被打印到 sys.stderr。

如果此函數(shù)引發(fā)了異常,則會調(diào)用 sys.excepthook() 來處理它。

threading.excepthook() 可以被重載以控制由 Thread.run() 引發(fā)的未捕獲異常的處理方式。

使用定制鉤子存放 exc_value 可能會創(chuàng)建引用循環(huán)。 它應當在不再需要異常時被顯式地清空以打破引用循環(huán)。

如果一個對象正在被銷毀,那么使用自定義的鉤子儲存 thread 可能會將其復活。請在自定義鉤子生效后避免儲存 thread,以避免對象的復活。

參見

sys.excepthook() 處理未捕獲的異常。

3.8 新版功能.

threading.__excepthook__?

保存 threading.excepthook() 的原始值。 它被保存以便在原始值碰巧被已損壞或替代對象所替換的情況下可被恢復。

3.10 新版功能.

threading.get_ident()?

返回當前線程的 “線程標識符”。它是一個非零的整數(shù)。它的值沒有直接含義,主要是用作 magic cookie,比如作為含有線程相關數(shù)據(jù)的字典的索引。線程標識符可能會在線程退出,新線程創(chuàng)建時被復用。

3.3 新版功能.

threading.get_native_id()?

返回內(nèi)核分配給當前線程的原生集成線程 ID。 這是一個非負整數(shù)。 它的值可被用來在整個系統(tǒng)中唯一地標識這個特定線程(直到線程終結(jié),在那之后該值可能會被 OS 回收再利用)。

Availability: Windows, FreeBSD, Linux, macOS, OpenBSD, NetBSD, AIX, DragonFlyBSD.

3.8 新版功能.

threading.enumerate()?

返回當前所有存活的 Thread 對象的列表。 該列表包括守護線程以及 current_thread() 創(chuàng)建的空線程。 它不包括已終結(jié)的和尚未開始的線程。 但是,主線程將總是結(jié)果的一部分,即使是在已終結(jié)的時候。

threading.main_thread()?

返回主 Thread 對象。一般情況下,主線程是Python解釋器開始時創(chuàng)建的線程。

3.4 新版功能.

threading.settrace(func)?

為所有 threading 模塊開始的線程設置追蹤函數(shù)。在每個線程的 run() 方法被調(diào)用前,func 會被傳遞給 sys.settrace() 。

threading.gettrace()?

返回由 settrace() 設置的跟蹤函數(shù)。

3.10 新版功能.

threading.setprofile(func)?

為所有 threading 模塊開始的線程設置性能測試函數(shù)。在每個線程的 run() 方法被調(diào)用前,func 會被傳遞給 sys.setprofile() 。

threading.getprofile()?

返回由 setprofile() 設置的性能分析函數(shù)。

3.10 新版功能.

threading.stack_size([size])?

返回創(chuàng)建線程時使用的堆棧大小??蛇x參數(shù) size 指定之后新建的線程的堆棧大小,而且一定要是0(根據(jù)平臺或者默認配置)或者最小是32,768(32KiB)的一個正整數(shù)。如果 size 沒有指定,默認是0。如果不支持改變線程堆棧大小,會拋出 RuntimeError 錯誤。如果指定的堆棧大小不合法,會拋出 ValueError 錯誤并且不會修改堆棧大小。32KiB是當前最小的能保證解釋器有足夠堆??臻g的堆棧大小。需要注意的是部分平臺對于堆棧大小會有特定的限制,例如要求大于32KiB的堆棧大小或者需要根據(jù)系統(tǒng)內(nèi)存頁面的整數(shù)倍進行分配 - 應當查閱平臺文檔有關詳細信息(4KiB頁面比較普遍,在沒有更具體信息的情況下,建議的方法是使用4096的倍數(shù)作為堆棧大小)。

適用于: Windows,具有 POSIX 線程的系統(tǒng)。

這個模塊同時定義了以下常量:

threading.TIMEOUT_MAX?

阻塞函數(shù)( Lock.acquire(), RLock.acquire(), Condition.wait(), ...)中形參 timeout 允許的最大值。傳入超過這個值的 timeout 會拋出 OverflowError 異常。

3.2 新版功能.

這個模塊定義了許多類,詳見以下部分。

該模塊的設計基于 Java的線程模型。 但是,在Java里面,鎖和條件變量是每個對象的基礎特性,而在Python里面,這些被獨立成了單獨的對象。 Python 的 Thread 類只是 Java 的 Thread 類的一個子集;目前還沒有優(yōu)先級,沒有線程組,線程還不能被銷毀、停止、暫停、恢復或中斷。 Java 的 Thread 類的靜態(tài)方法在實現(xiàn)時會映射為模塊級函數(shù)。

下述方法的執(zhí)行都是原子性的。

線程本地數(shù)據(jù)?

線程本地數(shù)據(jù)是特定線程的數(shù)據(jù)。管理線程本地數(shù)據(jù),只需要創(chuàng)建一個 local (或者一個子類型)的實例并在實例中儲存屬性:

mydata = threading.local()
mydata.x = 1

在不同的線程中,實例的值會不同。

class threading.local?

一個代表線程本地數(shù)據(jù)的類。

更多相關細節(jié)和大量示例,參見 _threading_local 模塊的文檔。

線程對象?

The Thread class represents an activity that is run in a separate thread of control. There are two ways to specify the activity: by passing a callable object to the constructor, or by overriding the run() method in a subclass. No other methods (except for the constructor) should be overridden in a subclass. In other words, only override the __init__() and run() methods of this class.

當線程對象一但被創(chuàng)建,其活動一定會因調(diào)用線程的 start() 方法開始。這會在獨立的控制線程調(diào)用 run() 方法。

一旦線程活動開始,該線程會被認為是 '存活的' 。當它的 run() 方法終結(jié)了(不管是正常的還是拋出未被處理的異常),就不是'存活的'。 is_alive() 方法用于檢查線程是否存活。

其他線程可以調(diào)用一個線程的 join() 方法。這會阻塞調(diào)用該方法的線程,直到被調(diào)用 join() 方法的線程終結(jié)。

線程有名字。名字可以傳遞給構造函數(shù),也可以通過 name 屬性讀取或者修改。

如果 run() 方法引發(fā)了異常,則會調(diào)用 threading.excepthook() 來處理它。 在默認情況下,threading.excepthook() 會靜默地忽略 SystemExit

一個線程可以被標記成一個“守護線程”。 這個標識的意義是,當剩下的線程都是守護線程時,整個 Python 程序?qū)顺觥?初始值繼承于創(chuàng)建線程。 這個標識可以通過 daemon 特征屬性或者 daemon 構造器參數(shù)來設置。

備注

守護線程在程序關閉時會突然關閉。他們的資源(例如已經(jīng)打開的文檔,數(shù)據(jù)庫事務等等)可能沒有被正確釋放。如果你想你的線程正常停止,設置他們成為非守護模式并且使用合適的信號機制,例如: Event。

有個 "主線程" 對象;這對應Python程序里面初始的控制線程。它不是一個守護線程。

There is the possibility that "dummy thread objects" are created. These are thread objects corresponding to "alien threads", which are threads of control started outside the threading module, such as directly from C code. Dummy thread objects have limited functionality; they are always considered alive and daemonic, and cannot be joined. They are never deleted, since it is impossible to detect the termination of alien threads.

class threading.Thread(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)?

應當始終使用關鍵字參數(shù)調(diào)用此構造函數(shù)。 參數(shù)如下:

group 應該為 None;為了日后擴展 ThreadGroup 類實現(xiàn)而保留。

target 是用于 run() 方法調(diào)用的可調(diào)用對象。默認是 None,表示不需要調(diào)用任何方法。

name 是線程名稱。 在默認情況下,會以 "Thread-N" 的形式構造唯一名稱,其中 N 為一個較小的十進制數(shù)值,或是 "Thread-N (target)" 的形式,其中 "target" 為 target.__name__,如果指定了 target 參數(shù)的話。

args is a list or tuple of arguments for the target invocation. Defaults to ().

kwargs 是用于調(diào)用目標函數(shù)的關鍵字參數(shù)字典。默認是 {}

如果不是 None,daemon 參數(shù)將顯式地設置該線程是否為守護模式。 如果是 None (默認值),線程將繼承當前線程的守護模式屬性。

如果子類型重載了構造函數(shù),它一定要確保在做任何事前,先發(fā)起調(diào)用基類構造器(Thread.__init__())。

在 3.10 版更改: 使用 target 名稱,如果 name 參數(shù)被省略的話。

在 3.3 版更改: 加入 daemon 參數(shù)。

start()?

開始線程活動。

它在一個線程里最多只能被調(diào)用一次。它安排對象的 run() 方法在一個獨立的控制進程中調(diào)用。

如果同一個線程對象中調(diào)用這個方法的次數(shù)大于一次,會拋出 RuntimeError 。

run()?

代表線程活動的方法。

你可以在子類型里重載這個方法。 標準的 run() 方法會對作為 target 參數(shù)傳遞給該對象構造器的可調(diào)用對象(如果存在)發(fā)起調(diào)用,并附帶從 argskwargs 參數(shù)分別獲取的位置和關鍵字參數(shù)。

Using list or tuple as the args argument which passed to the Thread could achieve the same effect.

Example:

>>>
>>> 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)?

等待,直到線程終結(jié)。這會阻塞調(diào)用這個方法的線程,直到被調(diào)用 join() 的線程終結(jié) -- 不管是正常終結(jié)還是拋出未處理異常 -- 或者直到發(fā)生超時,超時選項是可選的。

timeout 參數(shù)存在而且不是 None 時,它應該是一個用于指定操作超時的以秒為單位的浮點數(shù)或者分數(shù)。因為 join() 總是返回 None ,所以你一定要在 join() 后調(diào)用 is_alive() 才能判斷是否發(fā)生超時 -- 如果線程仍然存活,則 join() 超時。

timeout 參數(shù)不存在或者是 None ,這個操作會阻塞直到線程終結(jié)。

A thread can be joined many times.

如果嘗試加入當前線程會導致死鎖, join() 會引起 RuntimeError 異常。如果嘗試 join() 一個尚未開始的線程,也會拋出相同的異常。

name?

只用于識別的字符串。它沒有語義。多個線程可以賦予相同的名稱。 初始名稱由構造函數(shù)設置。

getName()?
setName()?

已被棄用的 name 的取值/設值 API;請改為直接以特征屬性方式使用它。

3.10 版后已移除.

ident?

這個線程的 '線程標識符',如果線程尚未開始則為 None 。這是個非零整數(shù)。參見 get_ident() 函數(shù)。當一個線程退出而另外一個線程被創(chuàng)建,線程標識符會被復用。即使線程退出后,仍可得到標識符。

native_id?

此線程的線程 ID (TID),由 OS (內(nèi)核) 分配。 這是一個非負整數(shù),或者如果線程還未啟動則為 None。 請參閱 get_native_id() 函數(shù)。 這個值可被用來在全系統(tǒng)范圍內(nèi)唯一地標識這個特定線程 (直到線程終結(jié),在那之后該值可能會被 OS 回收再利用)。

備注

類似于進程 ID,線程 ID 的有效期(全系統(tǒng)范圍內(nèi)保證唯一)將從線程被創(chuàng)建開始直到線程被終結(jié)。

可用性: 需要 get_native_id() 函數(shù)。

3.8 新版功能.

is_alive()?

返回線程是否存活。

run() 方法剛開始直到 run() 方法剛結(jié)束,這個方法返回 True 。模塊函數(shù) enumerate() 返回包含所有存活線程的列表。

daemon?

A boolean value indicating whether this thread is a daemon thread (True) or not (False). This must be set before start() is called, otherwise RuntimeError is raised. Its initial value is inherited from the creating thread; the main thread is not a daemon thread and therefore all threads created in the main thread default to daemon = False.

當沒有存活的非守護線程時,整個Python程序才會退出。

isDaemon()?
setDaemon()?

已被棄用的 daemon 的取值/設值 API;請改為直接以特征屬性方式使用它。

3.10 版后已移除.

鎖對象?

原始鎖是一個在鎖定時不屬于特定線程的同步基元組件。在Python中,它是能用的最低級的同步基元組件,由 _thread 擴展模塊直接實現(xiàn)。

原始鎖處于 "鎖定" 或者 "非鎖定" 兩種狀態(tài)之一。它被創(chuàng)建時為非鎖定狀態(tài)。它有兩個基本方法, acquire()release() 。當狀態(tài)為非鎖定時, acquire() 將狀態(tài)改為 鎖定 并立即返回。當狀態(tài)是鎖定時, acquire() 將阻塞至其他線程調(diào)用 release() 將其改為非鎖定狀態(tài),然后 acquire() 調(diào)用重置其為鎖定狀態(tài)并返回。 release() 只在鎖定狀態(tài)下調(diào)用; 它將狀態(tài)改為非鎖定并立即返回。如果嘗試釋放一個非鎖定的鎖,則會引發(fā) RuntimeError  異常。

鎖同樣支持 上下文管理協(xié)議。

當多個線程在 acquire() 等待狀態(tài)轉(zhuǎn)變?yōu)槲存i定被阻塞,然后 release() 重置狀態(tài)為未鎖定時,只有一個線程能繼續(xù)執(zhí)行;至于哪個等待線程繼續(xù)執(zhí)行沒有定義,并且會根據(jù)實現(xiàn)而不同。

所有方法的執(zhí)行都是原子性的。

class threading.Lock?

實現(xiàn)原始鎖對象的類。一旦一個線程獲得一個鎖,會阻塞隨后嘗試獲得鎖的線程,直到它被釋放;任何線程都可以釋放它。

需要注意的是 Lock 其實是一個工廠函數(shù),返回平臺支持的具體鎖類中最有效的版本的實例。

acquire(blocking=True, timeout=- 1)?

可以阻塞或非阻塞地獲得鎖。

當調(diào)用時參數(shù) blocking 設置為 True (缺省值),阻塞直到鎖被釋放,然后將鎖鎖定并返回 True 。

在參數(shù) blocking 被設置為 False 的情況下調(diào)用,將不會發(fā)生阻塞。如果調(diào)用時 blocking 設為 True 會阻塞,并立即返回 False ;否則,將鎖鎖定并返回 True

When invoked with the floating-point timeout argument set to a positive value, block for at most the number of seconds specified by timeout and as long as the lock cannot be acquired. A timeout argument of -1 specifies an unbounded wait. It is forbidden to specify a timeout when blocking is False.

如果成功獲得鎖,則返回 True,否則返回 False (例如發(fā)生 超時 的時候)。

在 3.2 版更改: 新的 timeout 形參。

在 3.2 版更改: 現(xiàn)在如果底層線程實現(xiàn)支持,則可以通過POSIX上的信號中斷鎖的獲取。

release()?

釋放一個鎖。這個方法可以在任何線程中調(diào)用,不單指獲得鎖的線程。

當鎖被鎖定,將它重置為未鎖定,并返回。如果其他線程正在等待這個鎖解鎖而被阻塞,只允許其中一個允許。

當在未鎖定的鎖上發(fā)起調(diào)用時,會引發(fā) RuntimeError。

沒有返回值。

locked()?

Return True if the lock is acquired.

遞歸鎖對象?

重入鎖是一個可以被同一個線程多次獲取的同步基元組件。在內(nèi)部,它在基元鎖的鎖定/非鎖定狀態(tài)上附加了 "所屬線程" 和 "遞歸等級" 的概念。在鎖定狀態(tài)下,某些線程擁有鎖 ; 在非鎖定狀態(tài)下, 沒有線程擁有它。

若要鎖定鎖,線程調(diào)用其 acquire() 方法;一旦線程擁有了鎖,方法將返回。若要解鎖,線程調(diào)用 release() 方法。 acquire()/release() 對可以嵌套;只有最終 release() (最外面一對的 release() ) 將鎖解開,才能讓其他線程繼續(xù)處理 acquire() 阻塞。

遞歸鎖也支持 上下文管理協(xié)議

class threading.RLock?

此類實現(xiàn)了重入鎖對象。重入鎖必須由獲取它的線程釋放。一旦線程獲得了重入鎖,同一個線程再次獲取它將不阻塞;線程必須在每次獲取它時釋放一次。

需要注意的是 RLock 其實是一個工廠函數(shù),返回平臺支持的具體遞歸鎖類中最有效的版本的實例。

acquire(blocking=True, timeout=- 1)?

可以阻塞或非阻塞地獲得鎖。

當無參數(shù)調(diào)用時: 如果這個線程已經(jīng)擁有鎖,遞歸級別增加一,并立即返回。否則,如果其他線程擁有該鎖,則阻塞至該鎖解鎖。一旦鎖被解鎖(不屬于任何線程),則搶奪所有權,設置遞歸等級為一,并返回。如果多個線程被阻塞,等待鎖被解鎖,一次只有一個線程能搶到鎖的所有權。在這種情況下,沒有返回值。

When invoked with the blocking argument set to True, do the same thing as when called without arguments, and return True.

When invoked with the blocking argument set to False, do not block. If a call without an argument would block, return False immediately; otherwise, do the same thing as when called without arguments, and return True.

When invoked with the floating-point timeout argument set to a positive value, block for at most the number of seconds specified by timeout and as long as the lock cannot be acquired. Return True if the lock has been acquired, False if the timeout has elapsed.

在 3.2 版更改: 新的 timeout 形參。

release()?

釋放鎖,自減遞歸等級。如果減到零,則將鎖重置為非鎖定狀態(tài)(不被任何線程擁有),并且,如果其他線程正被阻塞著等待鎖被解鎖,則僅允許其中一個線程繼續(xù)。如果自減后,遞歸等級仍然不是零,則鎖保持鎖定,仍由調(diào)用線程擁有。

只有當前線程擁有鎖才能調(diào)用這個方法。如果鎖被釋放后調(diào)用這個方法,會引起 RuntimeError 異常。

沒有返回值。

條件對象?

條件變量總是與某種類型的鎖對象相關聯(lián),鎖對象可以通過傳入獲得,或者在缺省的情況下自動創(chuàng)建。當多個條件變量需要共享同一個鎖時,傳入一個鎖很有用。鎖是條件對象的一部分,你不必單獨地跟蹤它。

條件變量遵循 上下文管理協(xié)議 :使用 with 語句會在它包圍的代碼塊內(nèi)獲取關聯(lián)的鎖。 acquire()release() 方法也能調(diào)用關聯(lián)鎖的相關方法。

其它方法必須在持有關聯(lián)的鎖的情況下調(diào)用。 wait() 方法釋放鎖,然后阻塞直到其它線程調(diào)用 notify() 方法或 notify_all() 方法喚醒它。一旦被喚醒, wait() 方法重新獲取鎖并返回。它也可以指定超時時間。

The notify() method wakes up one of the threads waiting for the condition variable, if any are waiting. The notify_all() method wakes up all threads waiting for the condition variable.

注意: notify() 方法和 notify_all() 方法并不會釋放鎖,這意味著被喚醒的線程不會立即從它們的 wait() 方法調(diào)用中返回,而是會在調(diào)用了 notify() 方法或 notify_all() 方法的線程最終放棄了鎖的所有權后返回。

使用條件變量的典型編程風格是將鎖用于同步某些共享狀態(tài)的權限,那些對狀態(tài)的某些特定改變感興趣的線程,它們重復調(diào)用 wait() 方法,直到看到所期望的改變發(fā)生;而對于修改狀態(tài)的線程,它們將當前狀態(tài)改變?yōu)榭赡苁堑却咚诖男聽顟B(tài)后,調(diào)用 notify() 方法或者 notify_all() 方法。例如,下面的代碼是一個通用的無限緩沖區(qū)容量的生產(chǎn)者-消費者情形:

# 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 循環(huán)檢查所要求的條件成立與否是有必要的,因為 wait() 方法可能要經(jīng)過不確定長度的時間后才會返回,而此時導致 notify() 方法調(diào)用的那個條件可能已經(jīng)不再成立。這是多線程編程所固有的問題。 wait_for() 方法可自動化條件檢查,并簡化超時計算。

# Consume an item
with cv:
    cv.wait_for(an_item_is_available)
    get_an_available_item()

選擇 notify() 還是 notify_all() ,取決于一次狀態(tài)改變是只能被一個還是能被多個等待線程所用。例如在一個典型的生產(chǎn)者-消費者情形中,添加一個項目到緩沖區(qū)只需喚醒一個消費者線程。

class threading.Condition(lock=None)?

實現(xiàn)條件變量對象的類。一個條件變量對象允許一個或多個線程在被其它線程所通知之前進行等待。

如果給出了非 Nonelock 參數(shù),則它必須為 Lock 或者 RLock 對象,并且它將被用作底層鎖。否則,將會創(chuàng)建新的 RLock 對象,并將其用作底層鎖。

在 3.3 版更改: 從工廠函數(shù)變?yōu)轭悺?/p>

acquire(*args)?

請求底層鎖。此方法調(diào)用底層鎖的相應方法,返回值是底層鎖相應方法的返回值。

release()?

釋放底層鎖。此方法調(diào)用底層鎖的相應方法。沒有返回值。

wait(timeout=None)?

等待直到被通知或發(fā)生超時。如果線程在調(diào)用此方法時沒有獲得鎖,將會引發(fā) RuntimeError 異常。

這個方法釋放底層鎖,然后阻塞,直到在另外一個線程中調(diào)用同一個條件變量的 notify()notify_all() 喚醒它,或者直到可選的超時發(fā)生。一旦被喚醒或者超時,它重新獲得鎖并返回。

當提供了 timeout 參數(shù)且不是 None 時,它應該是一個浮點數(shù),代表操作的超時時間,以秒為單位(可以為小數(shù))。

當?shù)讓渔i是個 RLock ,不會使用它的 release() 方法釋放鎖,因為當它被遞歸多次獲取時,實際上可能無法解鎖。相反,使用了 RLock 類的內(nèi)部接口,即使多次遞歸獲取它也能解鎖它。 然后,在重新獲取鎖時,使用另一個內(nèi)部接口來恢復遞歸級別。

返回 True ,除非提供的 timeout 過期,這種情況下返回 False。

在 3.2 版更改: 很明顯,方法總是返回 None

wait_for(predicate, timeout=None)?

等待,直到條件計算為真。 predicate 應該是一個可調(diào)用對象而且它的返回值可被解釋為一個布爾值??梢蕴峁?timeout 參數(shù)給出最大等待時間。

這個實用方法會重復地調(diào)用 wait() 直到滿足判斷式或者發(fā)生超時。返回值是判斷式最后一個返回值,而且如果方法發(fā)生超時會返回 False 。

忽略超時功能,調(diào)用此方法大致相當于編寫:

while not predicate():
    cv.wait()

因此,規(guī)則同樣適用于 wait() :鎖必須在被調(diào)用時保持獲取,并在返回時重新獲取。 隨著鎖定執(zhí)行判斷式。

3.2 新版功能.

notify(n=1)?

默認喚醒一個等待這個條件的線程。如果調(diào)用線程在沒有獲得鎖的情況下調(diào)用這個方法,會引發(fā) RuntimeError 異常。

這個方法喚醒最多 n 個正在等待這個條件變量的線程;如果沒有線程在等待,這是一個空操作。

當前實現(xiàn)中,如果至少有 n 個線程正在等待,準確喚醒 n 個線程。但是依賴這個行為并不安全。未來,優(yōu)化的實現(xiàn)有時會喚醒超過 n 個線程。

注意:被喚醒的線程并沒有真正恢復到它調(diào)用的 wait() ,直到它可以重新獲得鎖。 因為 notify() 不釋放鎖,其調(diào)用者才應該這樣做。

notify_all()?

喚醒所有正在等待這個條件的線程。這個方法行為與 notify() 相似,但并不只喚醒單一線程,而是喚醒所有等待線程。如果調(diào)用線程在調(diào)用這個方法時沒有獲得鎖,會引發(fā) RuntimeError 異常。

notifyAll 方法是此方法的已棄用別名。

信號量對象?

這是計算機科學史上最古老的同步原語之一,早期的荷蘭科學家 Edsger W. Dijkstra 發(fā)明了它。(他使用名稱 P()V() 而不是 acquire()release() )。

一個信號量管理一個內(nèi)部計數(shù)器,該計數(shù)器因 acquire() 方法的調(diào)用而遞減,因 release() 方法的調(diào)用而遞增。 計數(shù)器的值永遠不會小于零;當 acquire() 方法發(fā)現(xiàn)計數(shù)器為零時,將會阻塞,直到其它線程調(diào)用 release() 方法。

信號量對象也支持 上下文管理協(xié)議 。

class threading.Semaphore(value=1)?

該類實現(xiàn)信號量對象。信號量對象管理一個原子性的計數(shù)器,代表 release() 方法的調(diào)用次數(shù)減去 acquire() 的調(diào)用次數(shù)再加上一個初始值。如果需要, acquire() 方法將會阻塞直到可以返回而不會使得計數(shù)器變成負數(shù)。在沒有顯式給出 value 的值時,默認為1。

可選參數(shù) value 賦予內(nèi)部計數(shù)器初始值,默認值為 1 。如果 value 被賦予小于0的值,將會引發(fā) ValueError 異常。

在 3.3 版更改: 從工廠函數(shù)變?yōu)轭悺?/p>

acquire(blocking=True, timeout=None)?

獲取一個信號量。

在不帶參數(shù)的情況下調(diào)用時:

  • 如果在進入時內(nèi)部計數(shù)器的值大于零,則將其減一并立即返回 True

  • 如果在進入時內(nèi)部計數(shù)器的值為零,則將會阻塞直到被對 release() 的調(diào)用喚醒。 一旦被喚醒(并且計數(shù)器的值大于 0),則將計數(shù)器減 1 并返回 True。 每次對 release() 的調(diào)用將只喚醒一個線程。 線程被喚醒的次序是不可確定的。

When invoked with blocking set to False, do not block. If a call without an argument would block, return False immediately; otherwise, do the same thing as when called without arguments, and return True.

當發(fā)起調(diào)用時如果 timeout 不為 None,則它將阻塞最多 timeout 秒。 請求在此時段時未能成功完成獲取則將返回 False。 在其他情況下返回 True。

在 3.2 版更改: 新的 timeout 形參。

release(n=1)?

釋放一個信號量,將內(nèi)部計數(shù)器的值增加 n。 當進入時值為零且有其他線程正在等待它再次變?yōu)榇笥诹銜r,則喚醒那 n 個線程。

在 3.9 版更改: 增加了 n 形參以一次性釋放多個等待線程。

class threading.BoundedSemaphore(value=1)?

該類實現(xiàn)有界信號量。有界信號量通過檢查以確保它當前的值不會超過初始值。如果超過了初始值,將會引發(fā) ValueError 異常。在大多情況下,信號量用于保護數(shù)量有限的資源。如果信號量被釋放的次數(shù)過多,則表明出現(xiàn)了錯誤。沒有指定時, value 的值默認為1。

在 3.3 版更改: 從工廠函數(shù)變?yōu)轭悺?/p>

Semaphore 例子?

信號量通常用于保護數(shù)量有限的資源,例如數(shù)據(jù)庫服務器。在資源數(shù)量固定的任何情況下,都應該使用有界信號量。在生成任何工作線程前,應該在主線程中初始化信號量。

maxconnections = 5
# ...
pool_sema = BoundedSemaphore(value=maxconnections)

工作線程生成后,當需要連接服務器時,這些線程將調(diào)用信號量的 acquire 和 release 方法:

with pool_sema:
    conn = connectdb()
    try:
        # ... use connection ...
    finally:
        conn.close()

使用有界信號量能減少這種編程錯誤:信號量的釋放次數(shù)多于其請求次數(shù)。

事件對象?

這是線程之間通信的最簡單機制之一:一個線程發(fā)出事件信號,而其他線程等待該信號。

一個事件對象管理一個內(nèi)部標識,調(diào)用 set() 方法可將其設置為 true ,調(diào)用 clear() 方法可將其設置為 false ,調(diào)用 wait() 方法將進入阻塞直到標識為 true 。

class threading.Event?

實現(xiàn)事件對象的類。事件對象管理一個內(nèi)部標識,調(diào)用 set() 方法可將其設置為true。調(diào)用 clear() 方法可將其設置為 false 。調(diào)用 wait() 方法將進入阻塞直到標識為true。這個標識初始時為 false 。

在 3.3 版更改: 從工廠函數(shù)變?yōu)轭悺?/p>

is_set()?

當且僅當內(nèi)部標識為 true 時返回 True 。

isSet 方法是此方法的已棄用別名。

set()?

將內(nèi)部標識設置為 true 。所有正在等待這個事件的線程將被喚醒。當標識為 true 時,調(diào)用 wait() 方法的線程不會被被阻塞。

clear()?

將內(nèi)部標識設置為 false 。之后調(diào)用 wait() 方法的線程將會被阻塞,直到調(diào)用 set() 方法將內(nèi)部標識再次設置為 true 。

wait(timeout=None)?

阻塞線程直到內(nèi)部變量為 true 。如果調(diào)用時內(nèi)部標識為 true,將立即返回。否則將阻塞線程,直到調(diào)用 set() 方法將標識設置為 true 或者發(fā)生可選的超時。

當提供了timeout參數(shù)且不是 None 時,它應該是一個浮點數(shù),代表操作的超時時間,以秒為單位(可以為小數(shù))。

當且僅當內(nèi)部旗標在等待調(diào)用之前或者等待開始之后被設為真值時此方法將返回 True,也就是說,它將總是返回 True 除非設定了超時且操作發(fā)生了超時。

在 3.1 版更改: 很明顯,方法總是返回 None。

定時器對象?

此類表示一個操作應該在等待一定的時間之后運行 --- 相當于一個定時器。 Timer 類是 Thread 類的子類,因此可以像一個自定義線程一樣工作。

與線程一樣,通過調(diào)用 start() 方法啟動定時器。而 cancel() 方法可以停止計時器(在計時結(jié)束前), 定時器在執(zhí)行其操作之前等待的時間間隔可能與用戶指定的時間間隔不完全相同。

例如:

def hello():
    print("hello, world")

t = Timer(30.0, hello)
t.start()  # after 30 seconds, "hello, world" will be printed
class threading.Timer(interval, function, args=None, kwargs=None)?

創(chuàng)建一個定時器,在經(jīng)過 interval 秒的間隔事件后,將會用參數(shù) args 和關鍵字參數(shù) kwargs 調(diào)用 function。如果 argsNone (默認值),則會使用一個空列表。如果 kwargsNone (默認值),則會使用一個空字典。

在 3.3 版更改: 從工廠函數(shù)變?yōu)轭悺?/p>

cancel()?

停止定時器并取消執(zhí)行計時器將要執(zhí)行的操作。僅當計時器仍處于等待狀態(tài)時有效。

柵欄對象?

3.2 新版功能.

柵欄類提供一個簡單的同步原語,用于應對固定數(shù)量的線程需要彼此相互等待的情況。線程調(diào)用 wait() 方法后將阻塞,直到所有線程都調(diào)用了 wait() 方法。此時所有線程將被同時釋放。

柵欄對象可以被多次使用,但進程的數(shù)量不能改變。

這是一個使用簡便的方法實現(xiàn)客戶端進程與服務端進程同步的例子:

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)?

創(chuàng)建一個需要 parties 個線程的柵欄對象。如果提供了可調(diào)用的 action 參數(shù),它會在所有線程被釋放時在其中一個線程中自動調(diào)用。 timeout 是默認的超時時間,如果沒有在 wait() 方法中指定超時時間的話。

wait(timeout=None)?

沖出柵欄。當柵欄中所有線程都已經(jīng)調(diào)用了這個函數(shù),它們將同時被釋放。如果提供了 timeout 參數(shù),這里的 timeout 參數(shù)優(yōu)先于創(chuàng)建柵欄對象時提供的 timeout 參數(shù)。

函數(shù)返回值是一個整數(shù),取值范圍在0到 parties -- 1,在每個線程中的返回值不相同??捎糜趶乃芯€程中選擇唯一的一個線程執(zhí)行一些特別的工作。例如:

i = barrier.wait()
if i == 0:
    # Only one thread needs to print this
    print("passed the barrier")

如果創(chuàng)建柵欄對象時在構造函數(shù)中提供了 action 參數(shù),它將在其中一個線程釋放前被調(diào)用。如果此調(diào)用引發(fā)了異常,柵欄對象將進入損壞態(tài)。

如果發(fā)生了超時,柵欄對象將進入破損態(tài)。

如果柵欄對象進入破損態(tài),或重置柵欄時仍有線程等待釋放,將會引發(fā) BrokenBarrierError 異常。

reset()?

重置柵欄為默認的初始態(tài)。如果柵欄中仍有線程等待釋放,這些線程將會收到 BrokenBarrierError 異常。

請注意使用此函數(shù)時,如果存在狀態(tài)未知的其他線程,則可能需要執(zhí)行外部同步。 如果柵欄已損壞則最好將其廢棄并新建一個。

abort()?

使柵欄處于損壞狀態(tài)。 這將導致任何現(xiàn)有和未來對 wait() 的調(diào)用失敗并引發(fā) BrokenBarrierError。 例如可以在需要中止某個線程時使用此方法,以避免應用程序的死鎖。

更好的方式是:創(chuàng)建柵欄時提供一個合理的超時時間,來自動避免某個線程出錯。

parties?

沖出柵欄所需要的線程數(shù)量。

n_waiting?

當前時刻正在柵欄中阻塞的線程數(shù)量。

broken?

一個布爾值,值為 True 表明柵欄為破損態(tài)。

exception threading.BrokenBarrierError?

異常類,是 RuntimeError 異常的子類,在 Barrier 對象重置時仍有線程阻塞時和對象進入破損態(tài)時被引發(fā)。

with 語句中使用鎖、條件和信號量?

這個模塊提供的帶有 acquire()release() 方法的對象,可以被用作 with 語句的上下文管理器。當進入語句塊時 acquire() 方法會被調(diào)用,退出語句塊時 release() 會被調(diào)用。因此,以下片段:

with some_lock:
    # do something...

相當于:

some_lock.acquire()
try:
    # do something...
finally:
    some_lock.release()

現(xiàn)在 Lock 、 RLock 、 Condition 、 SemaphoreBoundedSemaphore 對象可以用作 with 語句的上下文管理器。