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

源代碼: Lib/threading.py


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

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

注解

雖然他們沒有在下面列出,這個模塊仍然支持Python 2.x系列的這個模塊下以 camelCase (駝峰法)命名的方法和函數(shù)。

CPython implementation detail: CPython下,因為 Global Interpreter Lock,一個時刻只有一個線程可以執(zhí)行Python代碼(盡管如此,某些性能導(dǎo)向的庫可能會克服這個限制)。如果你想讓你的應(yīng)用更好的利用多核計算機的計算性能,推薦你使用 multiprocessing 或者 concurrent.futures.ProcessPoolExecutor 。但是如果你想同時運行多個I/O綁定任務(wù),線程仍然是一個合適的模型。

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

threading.active_count()?

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

threading.current_thread()?

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

threading.get_ident()?

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

3.3 新版功能.

threading.enumerate()?

以列表形式返回當(dāng)前所有存活的 Thread 對象。 該列表包含守護線程,current_thread() 創(chuàng)建的虛擬線程對象和主線程。它不包含已終結(jié)的線程和尚未開始的線程。

threading.main_thread()?

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

3.4 新版功能.

threading.settrace(func)?

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

threading.setprofile(func)?

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

threading.stack_size([size])?

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

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

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

threading.TIMEOUT_MAX?

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

3.2 新版功能.

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

該模塊的設(shè)計基于 Java的線程模型。 但是,在Java里面,鎖和條件變量是每個對象的基礎(chǔ)特性,而在Python里面,這些被獨立成了單獨的對象。 Python 的 Thread 類只是 Java 的 Thread 類的一個子集;目前還沒有優(yōu)先級,沒有線程組,線程還不能被銷毀、停止、暫停、恢復(fù)或中斷。 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ù)的類。

更多相關(guān)細節(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.

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

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

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

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

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

注解

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

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

"虛擬線程對象" 是可以被創(chuàng)建的。這些是對應(yīng)于“外部線程”的線程對象,它們是在線程模塊外部啟動的控制線程,例如直接來自C代碼。虛擬線程對象功能受限;他們總是被認為是存活的和守護模式,不能被 join() 。因為無法檢測外來線程的終結(jié),它們永遠不會被刪除。

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

調(diào)用這個構(gòu)造函數(shù)時,必需帶有關(guān)鍵字參數(shù)。參數(shù)如下:

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

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

name 是線程名稱。默認情況下,由 "Thread-N" 格式構(gòu)成一個唯一的名稱,其中 N 是小的十進制數(shù)。

args 是用于調(diào)用目標(biāo)函數(shù)的參數(shù)元組。默認是 ()

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

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

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

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

start()?

開始線程活動。

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

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

run()?

代表線程活動的方法。

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

join(timeout=None)?

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

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

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

一個線程可以被 join() 很多次。

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

name?

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

getName()?
setName()?

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

ident?

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

is_alive()?

返回線程是否存活。

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

daemon?

一個表示這個線程是(True)否(False)守護線程的布爾值。一定要在調(diào)用 start() 前設(shè)置好,不然會拋出 RuntimeError 。初始值繼承于創(chuàng)建線程;主線程不是守護線程,因此主線程創(chuàng)建的所有線程默認都是 daemon = False

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

isDaemon()?
setDaemon()?

舊的 daemon 取值/設(shè)值 API;請改為直接以特性屬性方式使用它。

鎖對象?

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

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

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

當(dāng)多個線程在 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)?

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

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

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

當(dāng)浮點型 timeout 參數(shù)被設(shè)置為正值調(diào)用時,只要無法獲得鎖,將最多阻塞 timeout 設(shè)定的秒數(shù)。timeout 參數(shù)被設(shè)置為 -1 時將無限等待。當(dāng) blocking 為 false 時,timeout 指定的值將被忽略。

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

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

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

release()?

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

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

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

沒有返回值。

locked()?

如果獲得了鎖則返回真值。

遞歸鎖對象?

重入鎖是一個可以被同一個線程多次獲取的同步基元組件。在內(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)?

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

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

當(dāng)發(fā)起調(diào)用時將 blocking 參數(shù)設(shè)為真值,則執(zhí)行與無參數(shù)調(diào)用時一樣的操作,然后返回 True

當(dāng)發(fā)起調(diào)用時將 blocking 參數(shù)設(shè)為假值,則不進行阻塞。 如果一個無參數(shù)調(diào)用將要阻塞,則立即返回 False;在其他情況下,執(zhí)行與無參數(shù)調(diào)用時一樣的操作,然后返回 True

當(dāng)發(fā)起調(diào)用時將浮點數(shù)的 timeout 參數(shù)設(shè)為正值時,只要無法獲得鎖,將最多阻塞 timeout 所指定的秒數(shù)。 如果已經(jīng)獲得鎖則返回 True,如果超時則返回假值。

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

release()?

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

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

沒有返回值。

條件對象?

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

條件變量服從 上下文管理協(xié)議:使用 with 語句會在它包圍的代碼塊內(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() 方法重新獲取鎖并返回。它也可以指定超時時間。

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() 方法的線程最終放棄了鎖的所有權(quán)后返回。

使用條件變量的典型編程風(fēng)格是將鎖用于同步某些共享狀態(tài)的權(quán)限,那些對狀態(tài)的某些特定改變感興趣的線程,它們重復(fù)調(diào)用 wait() 方法,直到看到所期望的改變發(fā)生;而對于修改狀態(tài)的線程,它們將當(dāng)前狀態(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)過不確定長度的時間后才會返回,而此時導(dǎo)致 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)用底層鎖的相應(yīng)方法,返回值是底層鎖相應(yīng)方法的返回值。

release()?

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

wait(timeout=None)?

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

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

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

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

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

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

wait_for(predicate, timeout=None)?

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

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

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

while not predicate():
    cv.wait()

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

3.2 新版功能.

notify(n=1)?

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

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

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

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

notify_all()?

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

信號量對象?

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

一個信號量管理一個內(nèi)部計數(shù)器,該計數(shù)器因 acquire() 方法的調(diào)用而遞減,因 release() 方法的調(diào)用而遞增。 計數(shù)器的值永遠不會小于零;當(dāng) 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)用將只喚醒一個線程。 線程被喚醒的次序是不可確定的。

當(dāng)發(fā)起調(diào)用時將 blocking 設(shè)為假值,則不進行阻塞。 如果一個無參數(shù)調(diào)用將要阻塞,則立即返回 False;在其他情況下,執(zhí)行與無參數(shù)調(diào)用時一樣的操作,然后返回 True

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

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

release()?

釋放一個信號量,將內(nèi)部計數(shù)器的值增加1。當(dāng)計數(shù)器原先的值為0且有其它線程正在等待它再次大于0時,喚醒正在等待的線程。

class threading.BoundedSemaphore(value=1)?

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

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

Semaphore 例子?

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

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

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

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

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

事件對象?

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

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

class threading.Event?

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

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

is_set()?

當(dāng)且僅當(dāng)內(nèi)部旗標(biāo)為時返回 True

set()?

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

clear()?

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

wait(timeout=None)?

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

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

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

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

定時器對象?

此類表示一個操作應(yīng)該在等待一定的時間之后運行 --- 相當(dāng)于一個定時器。 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 和關(guān)鍵字參數(shù) kwargs 調(diào)用 function。如果 argsNone (默認值),則會使用一個空列表。如果 kwargsNone (默認值),則會使用一個空字典。

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

cancel()?

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

柵欄對象?

3.2 新版功能.

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

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

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

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

沖出柵欄。當(dāng)柵欄中所有線程都已經(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)建柵欄對象時在構(gòu)造函數(shù)中提供了 action 參數(shù),它將在其中一個線程釋放前被調(diào)用。如果此調(diào)用引發(fā)了異常,柵欄對象將進入損壞態(tài)。

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

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

reset()?

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

注意使用此函數(shù)時,如果有某些線程狀態(tài)未知,則可能需其它的同步來確保線程已被釋放。如果柵欄進入了破損態(tài),最好廢棄它并新建一個柵欄。

abort()?

使柵欄進入破損態(tài)。這將導(dǎo)致所有已經(jīng)調(diào)用和未來調(diào)用的 wait() 方法中引發(fā) BrokenBarrierError 異常。使用這個方法的一種情況是需要中止程序以避免死鎖。

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

parties?

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

n_waiting?

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

broken?

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

exception threading.BrokenBarrierError?

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

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

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

with some_lock:
    # do something...

相當(dāng)于:

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

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