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

源代碼: Lib/threading.py


這個(gè)模塊在較低級(jí)的模塊 _thread 基礎(chǔ)上建立較高級(jí)的線程接口。參見: queue 模塊。

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

備注

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

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

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

threading.active_count()?

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

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

threading.current_thread()?

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

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

threading.excepthook(args, /)?

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

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

  • exc_type: 異常類型

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

  • exc_traceback: 異?;厮荩梢允?None.

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

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

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

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

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

如果一個(gè)對(duì)象正在被銷毀,那么使用自定義的鉤子儲(chǔ)存 thread 可能會(huì)將其復(fù)活。請(qǐng)?jiān)谧远x鉤子生效后避免儲(chǔ)存 thread,以避免對(duì)象的復(fù)活。

參見

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

3.8 新版功能.

threading.__excepthook__?

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

3.10 新版功能.

threading.get_ident()?

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

3.3 新版功能.

threading.get_native_id()?

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

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

3.8 新版功能.

threading.enumerate()?

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

threading.main_thread()?

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

3.4 新版功能.

threading.settrace(func)?

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

threading.gettrace()?

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

3.10 新版功能.

threading.setprofile(func)?

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

threading.getprofile()?

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

3.10 新版功能.

threading.stack_size([size])?

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

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

這個(gè)模塊同時(shí)定義了以下常量:

threading.TIMEOUT_MAX?

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

3.2 新版功能.

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

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

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

線程本地?cái)?shù)據(jù)?

線程本地?cái)?shù)據(jù)是特定線程的數(shù)據(jù)。管理線程本地?cái)?shù)據(jù),只需要?jiǎng)?chuàng)建一個(gè) local (或者一個(gè)子類型)的實(shí)例并在實(shí)例中儲(chǔ)存屬性:

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

在不同的線程中,實(shí)例的值會(huì)不同。

class threading.local?

一個(gè)代表線程本地?cái)?shù)據(jù)的類。

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

線程對(duì)象?

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.

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

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

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

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

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

一個(gè)線程可以被標(biāo)記成一個(gè)“守護(hù)線程”。 這個(gè)標(biāo)識(shí)的意義是,當(dāng)剩下的線程都是守護(hù)線程時(shí),整個(gè) Python 程序?qū)?huì)退出。 初始值繼承于創(chuàng)建線程。 這個(gè)標(biāo)識(shí)可以通過 daemon 特征屬性或者 daemon 構(gòu)造器參數(shù)來設(shè)置。

備注

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

有個(gè) "主線程" 對(duì)象;這對(duì)應(yīng)Python程序里面初始的控制線程。它不是一個(gè)守護(hù)線程。

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

應(yīng)當(dāng)始終使用關(guān)鍵字參數(shù)調(diào)用此構(gòu)造函數(shù)。 參數(shù)如下:

group 應(yīng)該為 None;為了日后擴(kuò)展 ThreadGroup 類實(shí)現(xiàn)而保留。

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

name 是線程名稱。 在默認(rèn)情況下,會(huì)以 "Thread-N" 的形式構(gòu)造唯一名稱,其中 N 為一個(gè)較小的十進(jì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)用目標(biāo)函數(shù)的關(guān)鍵字參數(shù)字典。默認(rèn)是 {}。

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

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

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

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

start()?

開始線程活動(dòng)。

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

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

run()?

代表線程活動(dòng)的方法。

你可以在子類型里重載這個(gè)方法。 標(biāo)準(zhǔn)的 run() 方法會(huì)對(duì)作為 target 參數(shù)傳遞給該對(duì)象構(gòu)造器的可調(diào)用對(duì)象(如果存在)發(fā)起調(diào)用,并附帶從 argskwargs 參數(shù)分別獲取的位置和關(guān)鍵字參數(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é)。這會(huì)阻塞調(diào)用這個(gè)方法的線程,直到被調(diào)用 join() 的線程終結(jié) -- 不管是正常終結(jié)還是拋出未處理異常 -- 或者直到發(fā)生超時(shí),超時(shí)選項(xiàng)是可選的。

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

當(dāng) timeout 參數(shù)不存在或者是 None ,這個(gè)操作會(huì)阻塞直到線程終結(jié)。

A thread can be joined many times.

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

name?

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

getName()?
setName()?

已被棄用的 name 的取值/設(shè)值 API;請(qǐng)改為直接以特征屬性方式使用它。

3.10 版后已移除.

ident?

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

native_id?

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

備注

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

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

3.8 新版功能.

is_alive()?

返回線程是否存活。

當(dāng) run() 方法剛開始直到 run() 方法剛結(jié)束,這個(gè)方法返回 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.

當(dāng)沒有存活的非守護(hù)線程時(shí),整個(gè)Python程序才會(huì)退出。

isDaemon()?
setDaemon()?

已被棄用的 daemon 的取值/設(shè)值 API;請(qǐng)改為直接以特征屬性方式使用它。

3.10 版后已移除.

鎖對(duì)象?

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

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

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

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

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

class threading.Lock?

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

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

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

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

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

在參數(shù) blocking 被設(shè)置為 False 的情況下調(diào)用,將不會(huì)發(fā)生阻塞。如果調(diào)用時(shí) blocking 設(shè)為 True 會(huì)阻塞,并立即返回 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ā)生 超時(shí) 的時(shí)候)。

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

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

release()?

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

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

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

沒有返回值。

locked()?

Return True if the lock is acquired.

遞歸鎖對(duì)象?

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

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

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

class threading.RLock?

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

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

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

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

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

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

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

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

沒有返回值。

條件對(duì)象?

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

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

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

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() 方法并不會(huì)釋放鎖,這意味著被喚醒的線程不會(huì)立即從它們的 wait() 方法調(diào)用中返回,而是會(huì)在調(diào)用了 notify() 方法或 notify_all() 方法的線程最終放棄了鎖的所有權(quán)后返回。

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

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

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

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

class threading.Condition(lock=None)?

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

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

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

acquire(*args)?

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

release()?

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

wait(timeout=None)?

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

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

當(dāng)提供了 timeout 參數(shù)且不是 None 時(shí),它應(yīng)該是一個(gè)浮點(diǎn)數(shù),代表操作的超時(shí)時(shí)間,以秒為單位(可以為小數(shù))。

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

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

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

wait_for(predicate, timeout=None)?

等待,直到條件計(jì)算為真。 predicate 應(yīng)該是一個(gè)可調(diào)用對(duì)象而且它的返回值可被解釋為一個(gè)布爾值。可以提供 timeout 參數(shù)給出最大等待時(shí)間。

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

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

while not predicate():
    cv.wait()

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

3.2 新版功能.

notify(n=1)?

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

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

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

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

notify_all()?

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

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

信號(hào)量對(duì)象?

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

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

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

class threading.Semaphore(value=1)?

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

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

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

acquire(blocking=True, timeout=None)?

獲取一個(gè)信號(hào)量。

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

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

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

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.

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

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

release(n=1)?

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

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

class threading.BoundedSemaphore(value=1)?

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

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

Semaphore 例子?

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

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

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

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

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

事件對(duì)象?

這是線程之間通信的最簡(jiǎn)單機(jī)制之一:一個(gè)線程發(fā)出事件信號(hào),而其他線程等待該信號(hào)。

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

class threading.Event?

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

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

is_set()?

當(dāng)且僅當(dāng)內(nèi)部標(biāo)識(shí)為 true 時(shí)返回 True 。

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

set()?

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

clear()?

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

wait(timeout=None)?

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

當(dāng)提供了timeout參數(shù)且不是 None 時(shí),它應(yīng)該是一個(gè)浮點(diǎn)數(shù),代表操作的超時(shí)時(shí)間,以秒為單位(可以為小數(shù))。

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

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

定時(shí)器對(duì)象?

此類表示一個(gè)操作應(yīng)該在等待一定的時(shí)間之后運(yùn)行 --- 相當(dāng)于一個(gè)定時(shí)器。 Timer 類是 Thread 類的子類,因此可以像一個(gè)自定義線程一樣工作。

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

例如:

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)建一個(gè)定時(shí)器,在經(jīng)過 interval 秒的間隔事件后,將會(huì)用參數(shù) args 和關(guān)鍵字參數(shù) kwargs 調(diào)用 function。如果 argsNone (默認(rèn)值),則會(huì)使用一個(gè)空列表。如果 kwargsNone (默認(rèn)值),則會(huì)使用一個(gè)空字典。

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

cancel()?

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

柵欄對(duì)象?

3.2 新版功能.

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

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

這是一個(gè)使用簡(jiǎn)便的方法實(shí)現(xiàn)客戶端進(jìn)程與服務(wù)端進(jì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)建一個(gè)需要 parties 個(gè)線程的柵欄對(duì)象。如果提供了可調(diào)用的 action 參數(shù),它會(huì)在所有線程被釋放時(shí)在其中一個(gè)線程中自動(dòng)調(diào)用。 timeout 是默認(rèn)的超時(shí)時(shí)間,如果沒有在 wait() 方法中指定超時(shí)時(shí)間的話。

wait(timeout=None)?

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

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

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

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

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

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

reset()?

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

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

abort()?

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

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

parties?

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

n_waiting?

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

broken?

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

exception threading.BrokenBarrierError?

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

with 語句中使用鎖、條件和信號(hào)量?

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

with some_lock:
    # do something...

相當(dāng)于:

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

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