多線程Threading介紹

【threading模塊詳解】

模塊基本方法

該模塊定了的方法如下:
threading.active_count()
返回當(dāng)前活躍的Thread對(duì)象數(shù)量竭讳。返回值和通過enumerate()返回的列表長(zhǎng)度是相等的摊滔。
threading.current_thread()
返回當(dāng)前線程對(duì)象烟逊,對(duì)應(yīng)調(diào)用者的控制線程蜘渣。如果調(diào)用者的控制線程不是通過threading模塊創(chuàng)建授舟,一個(gè)功能受限的虛擬線程被返回塞弊。
threading.get_ident()
返回當(dāng)前線程的“線程標(biāo)識(shí)符”。這是一個(gè)非0整數(shù)强窖,沒有特定含義凸椿,通常用于索引線程特定數(shù)據(jù)的字典。線程標(biāo)識(shí)符可以被循環(huán)使用翅溺。
threading.enumerate()
返回當(dāng)前活躍的所有線程對(duì)象的列表脑漫。該列表包括精靈線程、被current_thread()創(chuàng)建的虛擬線程對(duì)象咙崎、和主線程优幸。它不包括終止的線程和還沒有啟動(dòng)的線程。
threading.main_thread()
返回主線程對(duì)象褪猛。在正常情況下网杆,主線程就是Python解釋器啟動(dòng)的線程。
threading.settrace(func)
為所有從threading模塊啟動(dòng)的線程設(shè)置一個(gè)trace函數(shù)。在每個(gè)線程的run()方法被調(diào)用前碳却,函數(shù)將為每個(gè)線程被傳遞到sys.settrace()队秩。
threading.setprofile(func)
為所有從threading模塊啟動(dòng)的線程設(shè)置一個(gè)profile函數(shù)。在每個(gè)線程的run()方法被調(diào)用前追城,函數(shù)將為每個(gè)線程被傳遞到sys.setprofile() 刹碾。
threading.stack_size([size])
返回當(dāng)創(chuàng)建一個(gè)新線程是使用的線程棧大小燥撞,0表示使用平臺(tái)或配置的默認(rèn)值座柱。
平臺(tái)頂一個(gè)常量如下:
threading.TIMEOUT_MAX
阻塞函數(shù)(Lock.acquire()、RLock.acquire()物舒、Condition.wait()等)的超時(shí)參數(shù)允許的最大值色洞。指定的值超過該值將拋出OverflowError。
該模塊也定義了一些類冠胯,在下面會(huì)講到火诸。
該模塊的設(shè)計(jì)是仿照J(rèn)ava的線程模型。然而荠察,Java使lock和condition變量成為每個(gè)對(duì)象的基本行為置蜀,在Python中則是分離的對(duì)象。Python的Thread類支持Java的線程類的行為的一個(gè)子集悉盆;當(dāng)前盯荤,沒有優(yōu)先級(jí),沒有線程組焕盟,并且線程不能被銷毀秋秤、停止、暫停脚翘、恢復(fù)灼卢、或者中斷。當(dāng)實(shí)現(xiàn)時(shí)来农,Java的線程類的靜態(tài)方法被對(duì)應(yīng)到模塊級(jí)函數(shù)鞋真。
形容在下面的所有方法都被原子地執(zhí)行。
線程本地?cái)?shù)據(jù)

線程本地?cái)?shù)據(jù)是那些值和特定線程相關(guān)的數(shù)據(jù)沃于。為了管理線程本地?cái)?shù)據(jù)涩咖,創(chuàng)建一個(gè)local類(或者一個(gè)子類)的實(shí)例,然后存儲(chǔ)屬性在它里面:
[python] view plain copy
mydata = threading.local()
mydata.x = 1
為不同線程實(shí)例的值將是不同的揽涮。

class threading.local
表示線程本地?cái)?shù)據(jù)的類抠藕。
更多的細(xì)節(jié)參考_threading_local的文檔字符串。
線程對(duì)象

Thread類表示一個(gè)運(yùn)行在一個(gè)獨(dú)立的控制線程中的行為蒋困。有兩個(gè)方法指定這個(gè)行為:通過傳遞一個(gè)callable對(duì)象給構(gòu)造函數(shù)盾似,或者通過在子類中重載run()方法,在子類中沒有其他方法(除了構(gòu)造函數(shù))應(yīng)該被重載,換句話說零院,僅重載這個(gè)類的init()和run()方法溉跃。
一旦一個(gè)線程對(duì)象被創(chuàng)建,他的行為必須通過線程的start()方法啟動(dòng)告抄,這將在一個(gè)獨(dú)立的控制線程中調(diào)用run()方法撰茎。
一旦線程的行為被啟動(dòng),這個(gè)線程被認(rèn)為是'活躍的'打洼。正常情況下龄糊,當(dāng)它的run()終止時(shí)它推出活躍狀態(tài),或者出現(xiàn)為處理的異常募疮。is_alive()方法可用于測(cè)試線程是否活躍炫惩。
其它線程能調(diào)用一個(gè)線程的join()方法。這將阻塞調(diào)用線程直到j(luò)oin()方法被調(diào)用的線程終止阿浓。
一個(gè)線程有一個(gè)名稱他嚷,名稱能被傳遞給構(gòu)造器,并可以通過name屬性讀取或者改變芭毙。
一個(gè)線程能被標(biāo)注為“精靈線程”筋蓖。這個(gè)標(biāo)志的意義是當(dāng)今有精靈線程遺留時(shí),Python程序?qū)⑼顺鐾硕亍3跏贾祻膭?chuàng)建的線程繼承粘咖,這個(gè)標(biāo)志可以通過daemon屬性設(shè)置,或者通過構(gòu)造器的daemon參數(shù)傳入苛聘。
注意涂炎,精靈線程在關(guān)閉時(shí)會(huì)突然地停止,他們的資源(例如打開的文件设哗、數(shù)據(jù)庫(kù)事務(wù)等)不能被正確的釋放唱捣。如果你想你的線程優(yōu)雅地停止,應(yīng)該使它們是非精靈線程并且使用一個(gè)適當(dāng)?shù)男盘?hào)機(jī)制网梢,例如Event(后面講解)震缭。
有一個(gè)“主線程”對(duì)象,這對(duì)應(yīng)到Python程序的初始控制線程战虏,它不是精靈線程拣宰。
有可能“虛擬線程對(duì)象”被創(chuàng)建,則會(huì)存在線程對(duì)象對(duì)應(yīng)到“外星人線程”烦感,其控制線程在threading模塊之外啟動(dòng)巡社,例如直接從C代碼啟動(dòng)。虛擬線程對(duì)象的功能是受限的手趣,它們總是被認(rèn)為是活躍的精靈線程晌该,不能被join()。他們不能被刪除,由于探測(cè)外星人線程的終止是不可能的朝群。
下面是Thread類的構(gòu)造方法:

class threading.Thread(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)
1)group:應(yīng)該是None燕耿,保留為未來的擴(kuò)展,當(dāng)一個(gè)ThreadGroup類被實(shí)現(xiàn)的時(shí)候需要姜胖;
2)target:一個(gè)callable對(duì)象誉帅,被run()方法調(diào)用,默認(rèn)為None右莱,意味著什么都不做蚜锨;
3)name:線程名,默認(rèn)情況下隧出,一個(gè)形式為“Thread-N”的唯一名被構(gòu)造踏志,N是一個(gè)小的十進(jìn)制數(shù)阀捅;
4)args:調(diào)用target的參數(shù)元組胀瞪,默認(rèn)為();
5)kwargs:為target調(diào)用的參數(shù)字典饲鄙,默認(rèn)為{}凄诞;
6)daemon:如果不為None,則設(shè)置該線程是否為精靈線程忍级。如果為None帆谍,則從當(dāng)前線程繼承;
如果子類覆蓋了構(gòu)造器轴咱,在做其它任何事情之前汛蝙,它必須確保先調(diào)用基類的構(gòu)造器(Thread.init())。
線程的常用方法如下:
1)start()
啟動(dòng)線程朴肺。
每個(gè)線程最多只能調(diào)用一次窖剑。它會(huì)導(dǎo)致對(duì)象的run()方法在獨(dú)立的控制線程中被調(diào)用。
如果在同一個(gè)線程對(duì)象上調(diào)用超過一次將拋出RuntimeError戈稿。
2)run()
表示線程行為的方法西土。
你可以在子類中重載該方法。標(biāo)準(zhǔn)的run()方法調(diào)用傳入到構(gòu)造器中callable對(duì)象(對(duì)應(yīng)target參數(shù))鞍盗,使用對(duì)應(yīng)的args或者kwargs參數(shù)需了。
3)join(timeout=None)
等待直到線程結(jié)束。這將阻塞當(dāng)前線程直到j(luò)oin()方法被調(diào)用的線程終止或者拋出一個(gè)未處理的異常般甲,或者設(shè)置的溢出時(shí)間到達(dá)肋乍。
如果timeout參數(shù)指定且為非None,則它應(yīng)該是一個(gè)浮點(diǎn)數(shù)敷存,用于指定操作的溢出時(shí)間墓造,單位為秒。由于join()總是返回None,因此在join()結(jié)束后你必須調(diào)用is_alive()來判斷線程是否結(jié)束滔岳,如果線程任然是活躍的杠娱,join()調(diào)用則是時(shí)間溢出。
當(dāng)timeout不被指定谱煤,或者指定為None時(shí)摊求,操作將阻塞直到線程終止。
一個(gè)線程能被join()多次刘离。
如果一個(gè)join當(dāng)前線程的嘗試將導(dǎo)致一個(gè)死鎖室叉,join()將拋出RuntimeError。在一個(gè)線程啟動(dòng)之前對(duì)該線程做join()操作也將導(dǎo)致同樣的異常硫惕。
4)name
線程名茧痕,僅用于標(biāo)識(shí)一個(gè)線程,沒有語(yǔ)義恼除,多個(gè)線程可以被給同樣的名字踪旷,初始的名稱被構(gòu)造器設(shè)置。
5)getName()
setName()
name的舊的getter/setter API豁辉,現(xiàn)在直接使用name屬性代替令野。
6)ident
這個(gè)線程的“線程標(biāo)識(shí)符”,如果線程沒有啟動(dòng)徽级,則為None气破。這是一個(gè)非0整數(shù)。線程標(biāo)識(shí)符可以被循環(huán)使用餐抢,一個(gè)線程退出后它的線程標(biāo)識(shí)符可以被其它線程使用现使。
7)is_alive()
返回線程是否活躍。
該方法只有在run()啟動(dòng)后并且在終止之前才返回True旷痕。模塊函數(shù)enumerate()返回所有活躍線程的一個(gè)列表碳锈。
8)daemon
一個(gè)布爾值,用于表示該線程是否精靈線程苦蒿。該值必須在start()方法調(diào)用之前設(shè)置殴胧,否則RuntimeError被拋出。它的初始值從創(chuàng)建的線程繼承佩迟。主線程不是一個(gè)精靈線程团滥,因此所有在主線程中創(chuàng)建的線程daemon默認(rèn)為False。
當(dāng)沒有活躍的非精靈線程運(yùn)行時(shí)报强,整個(gè)Python程序退出灸姊。
9)isDaemon()
setDaemon()
老的getter/setter API,現(xiàn)在直接使用daemon屬性代替秉溉。
Lock對(duì)象

基元鎖是不被特定線程擁有的同步基元力惯。在Python中碗誉,它是當(dāng)前可用的最低級(jí)別的同步基元,通過_thread擴(kuò)展模塊直接實(shí)現(xiàn)父晶。
一個(gè)基元鎖存在兩種狀態(tài)哮缺,“鎖”或者“未鎖”,初始創(chuàng)建時(shí)處于未鎖狀態(tài)甲喝。他有兩個(gè)基本方法:acquire()和release()尝苇。當(dāng)狀態(tài)是未鎖時(shí),acquire()將改變其狀態(tài)到鎖并且立即返回埠胖;當(dāng)狀態(tài)是鎖時(shí)糠溜,acquire()阻塞直到另一個(gè)線程調(diào)用release()釋放了鎖,然后acquire()獲取鎖并重設(shè)鎖的狀態(tài)到鎖并且返回直撤。release()應(yīng)該只在鎖處理鎖狀態(tài)時(shí)才調(diào)用非竿,它改變鎖的狀態(tài)到未鎖并且立即返回。如果嘗試釋放一個(gè)未鎖的鎖谋竖,一個(gè)RuntimeError將被拋出红柱。
鎖也支持上下文管理協(xié)議。
當(dāng)超過一個(gè)線程被鎖阻塞圈盔,當(dāng)鎖被釋放后僅有一個(gè)線程能獲取到鎖豹芯,獲取到鎖的線程不確定,依賴具體的實(shí)現(xiàn)驱敲。
相關(guān)類如下:

class threading.Lock
該類實(shí)現(xiàn)了基元鎖對(duì)象。線程可以通過acquire請(qǐng)求該鎖宽闲,如果已經(jīng)存在其它線程獲取了鎖众眨,則線程阻塞,直到其它線程釋放鎖容诬。
1)acquire(blocking=True, timeout=-1)
請(qǐng)求一個(gè)鎖娩梨,阻塞或者非阻塞。
當(dāng)blocking參數(shù)為True(默認(rèn))览徒,將阻塞直到鎖被釋放狈定,然后獲取鎖并返回True。
當(dāng)blocking參數(shù)為False习蓬,將不阻塞纽什。如果已經(jīng)存在線程獲取了鎖,調(diào)用將立即返回False躲叼;否則芦缰,將獲取鎖并返回True。
當(dāng)timeout參數(shù)大于0時(shí)枫慷,最多阻塞timeout指定的秒值让蕾。timeout為-1(默認(rèn))表示一直等待浪规。當(dāng)blocking為False時(shí)不允許指定timeout參數(shù)。
如果鎖請(qǐng)求成功探孝,則返回True笋婿,否則返回False(例如超時(shí))。
2)release()
釋放一個(gè)鎖顿颅。這能在任何線程中調(diào)用萌抵,不僅在獲取鎖的線程中。
當(dāng)鎖處于鎖狀態(tài)時(shí)元镀,重設(shè)它為未鎖绍填,并返回,其它阻塞等待該鎖的線程中將有一個(gè)線程能獲取到鎖栖疑。
在一個(gè)未鎖的鎖上調(diào)用該方法讨永,將拋出RuntimeError。
無返回值遇革。
RLock對(duì)象

一個(gè)可重入鎖可以被同一個(gè)線程請(qǐng)求多次卿闹。在內(nèi)部,它在基元鎖的基礎(chǔ)上使用了“擁有者線程”和“遞歸級(jí)別”的概念萝快。在鎖狀態(tài)锻霎,一些線程擁有鎖;在未鎖狀態(tài)揪漩,沒有線程擁有鎖旋恼。
為了獲取鎖,一個(gè)線程調(diào)用acquire()方法奄容,獲取鎖后返回冰更;為了釋放鎖,一個(gè)線程調(diào)用release()方法昂勒。acquire()/release()的調(diào)用可以是嵌套的蜀细,只有最后的release()重設(shè)鎖到未鎖。
可重入鎖也支持上下文管理協(xié)議戈盈。

class threading.RLock
該類實(shí)現(xiàn)了可重入鎖對(duì)象奠衔。一個(gè)可重入鎖必須被請(qǐng)求它的線程釋放,一旦一個(gè)線程擁有了一個(gè)可重入鎖塘娶,該線程可以再次請(qǐng)求它归斤,注意請(qǐng)求鎖的次數(shù)必須和釋放鎖的次數(shù)對(duì)應(yīng)。
注意RLock實(shí)際上是一個(gè)工廠函數(shù)血柳,返回當(dāng)前平臺(tái)支持的效率最高的RLock類版本的一個(gè)實(shí)例官册。
1)acquire(blocking=True, timeout=-1)
請(qǐng)求一個(gè)鎖,阻塞或者非阻塞方式难捌。
當(dāng)參數(shù)為空時(shí):如果這個(gè)線程已經(jīng)擁有鎖膝宁,遞歸級(jí)別加一鸦难,然后返回;否則员淫,如果另一個(gè)線程擁有鎖合蔽,阻塞直到鎖被釋放。如果鎖處于未鎖狀態(tài)(不被任何線程擁有)介返,則設(shè)置擁有者線程拴事,并設(shè)置遞歸級(jí)別為1,然后返回圣蝎。如果超過一個(gè)線程處于阻塞等待隊(duì)列中刃宵,一次僅有一個(gè)線程能獲取鎖。該場(chǎng)景沒有返回值徘公。
當(dāng)blocking為True時(shí)牲证,和沒有參數(shù)的場(chǎng)景相同,并返回True关面。
當(dāng)blocking為False時(shí)坦袍,將不阻塞。如果鎖處于鎖狀態(tài)等太,則立即返回False捂齐;否則,和沒有參數(shù)的場(chǎng)景相同缩抡,并返回True奠宜。
當(dāng)timeout參數(shù)大于0時(shí),最多阻塞timeout秒缝其。如果在timeout秒內(nèi)獲取了鎖挎塌,則返回True,否則超時(shí)返回False内边。
2)release()
釋放一個(gè)鎖,減少遞歸級(jí)別待锈。如果遞歸級(jí)別減少到0漠其,則重設(shè)鎖的狀態(tài)到未鎖(不被任何線程擁有)。如果減少后遞歸級(jí)別任然大于0竿音,則鎖任然被調(diào)用者線程保持和屎。
僅當(dāng)調(diào)用者線程擁有鎖時(shí)才調(diào)用該方法,否則拋出RuntimeError春瞬。
沒有返回值柴信。
Condition對(duì)象

condition變量總是和鎖相關(guān)聯(lián),這能被傳入或者通過默認(rèn)創(chuàng)建宽气。當(dāng)幾個(gè)condition變量必須共享同一個(gè)鎖時(shí)傳入是有用的随常。鎖是condition對(duì)象的一部分:你不必分別跟蹤它潜沦。
condition變量遵守上下文管理協(xié)議:在代碼塊中用with語(yǔ)句獲取相關(guān)的鎖。acquire()和release()方法也調(diào)用相關(guān)鎖的對(duì)應(yīng)方法绪氛。
其它方法必須被相關(guān)鎖的持有者調(diào)用唆鸡。wait()方法釋放鎖,然后阻塞直到另一個(gè)線程調(diào)用notify()或者notify_all()喚醒它枣察。喚醒后争占,wait()重新獲取鎖并返回。它也可以指定一個(gè)超時(shí)時(shí)間序目。
notify()方法喚醒等待線程中的一個(gè)臂痕;notify_all()方法喚醒所有等待線程。
注意:notify()和notify_all()方法不釋放鎖猿涨;這意味著喚醒的線程或者線程組將不會(huì)從wait()調(diào)用中立即返回握童。
使用condition變量的一個(gè)典型的應(yīng)用就是用鎖同步對(duì)一些共享狀態(tài)的進(jìn)入;對(duì)某個(gè)特定狀態(tài)感興趣的線程反復(fù)調(diào)用wait()嘿辟,直到出現(xiàn)他們感興趣的狀態(tài)舆瘪,修改這個(gè)狀態(tài)的線程則調(diào)用notify()或者notify_all()來通知等待的線程狀態(tài)已經(jīng)改變。例如红伦,下面是一個(gè)典型的使用了無限緩存的生產(chǎn)者-消費(fèi)者模式:
[python] view plain copy

消費(fèi)一個(gè)條目

with cv:
while not an_item_is_available():
cv.wait()
get_an_available_item()

產(chǎn)生一個(gè)條目

with cv:
make_an_item_available()
cv.notify()
while循環(huán)檢查是否有條目可用英古,因?yàn)閣ait()可以在等待任意時(shí)間后返回,也有可能調(diào)用notify()的線程并沒有使條件為真昙读,在多線程編程中這個(gè)問題始終存在召调。wait_for()方法能被用于自動(dòng)的條件檢測(cè)搞监,簡(jiǎn)化超時(shí)的計(jì)算:
[python] view plain copy

消費(fèi)一個(gè)條目

with cv:
cv.wait_for(an_item_is_available)
get_an_available_item()
對(duì)于notify()和notify_all()桶雀,使用哪個(gè)在于應(yīng)用場(chǎng)景中有一個(gè)還是多個(gè)等待線程。例如灿渴,在一個(gè)典型的生產(chǎn)者-消費(fèi)者場(chǎng)景中沮稚,增加一個(gè)條目到緩存僅需要喚醒一個(gè)消費(fèi)者線程艺沼。

class threading.Condition(lock=None)
該類實(shí)現(xiàn)條件變量對(duì)象。一個(gè)條件變量允許一個(gè)或多個(gè)線程等待蕴掏,直到他們被另一個(gè)線程通知障般。
如果lock參數(shù)被指定且不為None,它必須是Lock或者RLock對(duì)象盛杰,被用做隱含鎖挽荡。否則,一個(gè)新的RLock對(duì)象被創(chuàng)建并作為隱含鎖即供。
1)acquire(*args)
請(qǐng)求一個(gè)隱含鎖定拟,這個(gè)方法會(huì)調(diào)用隱含鎖的對(duì)應(yīng)方法,返回值即為隱含鎖的方法的返回值逗嫡。
2)release()
釋放隱含鎖青自。這個(gè)方法調(diào)用隱含鎖的對(duì)應(yīng)方法株依,沒有返回值。
3)wait(timeout=None)
等待直到被喚醒性穿,或者超時(shí)勺三。如果調(diào)用線程沒有請(qǐng)求鎖,RuntimeError被拋出需曾。
該方法會(huì)釋放隱含鎖吗坚,然后阻塞直到它被另一個(gè)線程調(diào)用同一個(gè)condition對(duì)象的notify()或者notify_all()方法喚醒,或者直到指定的timeout時(shí)間溢出呆万。一旦喚醒或者超時(shí)商源,它重新請(qǐng)求鎖并返回。
當(dāng)timeout參數(shù)被指定并不為None谋减,則指定了一個(gè)秒級(jí)的超時(shí)時(shí)間牡彻。
當(dāng)隱含鎖是一個(gè)RLock鎖,它不通過release()方法釋放鎖出爹,因?yàn)槿绻€程請(qǐng)求了多次鎖庄吼,使用release()方法不能解鎖(必須調(diào)用和lock方法相同的次數(shù)才能解鎖)。一個(gè)RLock類的內(nèi)部接口被使用严就,該接口能釋放鎖总寻,不管鎖請(qǐng)求了多少次。當(dāng)鎖被請(qǐng)求時(shí)梢为,另一個(gè)內(nèi)部接口被用于還原鎖的遞歸層級(jí)渐行。
方法返回True,如果超時(shí)則返回False铸董。
4)wait_for(predicate, timeout=None)
等待直到條件為True祟印。predicate應(yīng)該是一個(gè)callable,返回值為布爾值粟害。timeout用于指定超時(shí)時(shí)間蕴忆。
該方法相當(dāng)于反復(fù)調(diào)用wait()直到條件為真,或者超時(shí)悲幅。返回值是最后的predicate的返回值孽文,或者超時(shí)返回Flase。
忽略超時(shí)特性夺艰,調(diào)用這個(gè)方法相當(dāng)于:
[python] view plain copy
while not predicate():
cv.wait()
因此,調(diào)用該方法和調(diào)用wait()具有同樣的規(guī)則:調(diào)用是或者從阻塞中返回時(shí)必須先獲取鎖沉衣。
5)notify(n=1)
默認(rèn)情況下郁副,喚醒一個(gè)等待線程。如果調(diào)用該方法的線程沒有獲取鎖豌习,則RuntimeError被拋出存谎。
這個(gè)方法子多喚醒n(默認(rèn)為1)個(gè)等待線程拔疚;如果沒有線程等待,則沒有操作既荚。
如果至少n個(gè)線程正在等待稚失,當(dāng)前的實(shí)現(xiàn)是剛好喚醒n個(gè)線程。然而恰聘,依賴這個(gè)行為是不安全的句各,因?yàn)椋磥砟承﹥?yōu)化后的實(shí)現(xiàn)可能會(huì)喚醒超過n個(gè)線程晴叨。
注意:一個(gè)喚醒的線程只有當(dāng)請(qǐng)求到鎖后才會(huì)從wait()調(diào)用中返回凿宾。由于notify()不釋放鎖,所以它的調(diào)用者應(yīng)該釋放鎖兼蕊。
6)notify_all()
喚醒所有等待線程初厚。這個(gè)方法的行為類似于notify(),但是喚醒所有等待線程孙技。如果調(diào)用線程未獲取鎖产禾,則RuntimeError被拋出。
Semaphore對(duì)象

這是計(jì)算機(jī)科學(xué)歷史上最早的同步基元中的一個(gè)牵啦,被荷蘭的計(jì)算機(jī)科學(xué)家Edsger W. Dijkstra發(fā)明(他使用P()和V()而不是acquire()和release())亚情。
semaphore管理一個(gè)內(nèi)部計(jì)數(shù),每次調(diào)用acquire()時(shí)該計(jì)數(shù)減一蕾久,每次調(diào)用release()時(shí)計(jì)數(shù)加一势似。計(jì)數(shù)不會(huì)小于0,當(dāng)acquire()發(fā)現(xiàn)計(jì)數(shù)為0時(shí)僧著,則阻塞履因,等待直到其它線程調(diào)用release()。
semaphore也支持上下文管理協(xié)議盹愚。

class threading.Semaphore(value=1)
該類實(shí)現(xiàn)semaphore對(duì)象栅迄。一個(gè)semaphore管理一個(gè)表示計(jì)數(shù)表示能并行進(jìn)入的線程數(shù)量。如果計(jì)數(shù)為0皆怕,則acquire()阻塞直到計(jì)數(shù)大于0毅舆。value默認(rèn)為1.
value給出了內(nèi)部計(jì)數(shù)的初始值,默認(rèn)為1愈腾,如果傳入的value小于0憋活,則拋出ValueError。
1)acquire(blocking=True, timeout=None)
請(qǐng)求semaphore虱黄。
當(dāng)沒有參數(shù)時(shí):如果內(nèi)部計(jì)數(shù)大于0悦即,將計(jì)數(shù)減1并立即返回。如果計(jì)數(shù)為0,阻塞辜梳,等待直到另一個(gè)線程調(diào)用了release()粱甫。這使用互鎖機(jī)制實(shí)現(xiàn),保證了如果有多個(gè)線程調(diào)用acquire()阻塞作瞄,則release()將只會(huì)喚醒一個(gè)線程茶宵。喚醒的線程是隨機(jī)選擇一個(gè),不依賴阻塞的順序宗挥。成功返回True(或者無限阻塞)乌庶。
如果blocking為False,將不阻塞属韧。如果無法獲取semaphore安拟,則立即返回False;否則宵喂,同沒有參數(shù)時(shí)的操作糠赦,并返回True。
當(dāng)設(shè)置了timeout并且不是None锅棕,它將阻塞最多timeout秒拙泽。如果在該時(shí)間內(nèi)沒有成功獲取semaphore,則返回False裸燎;否則返回True顾瞻。
2)release()
釋放一個(gè)semaphore,計(jì)數(shù)加1德绿。如果計(jì)數(shù)初始為0荷荤,則需要喚醒等待隊(duì)列中的一個(gè)線程。

class threading.BoundedSemaphore(value=1)
該類實(shí)現(xiàn)了有界的semaphore對(duì)象移稳。一個(gè)有界的semaphore會(huì)確保它的當(dāng)前值沒有溢出他的初始值蕴纳,如果溢出,則ValueError被拋出个粱。在大部分場(chǎng)景下古毛,semaphore被用于限制資源的使用。如果semaphore被釋放太多次都许,往往表示出現(xiàn)了bug稻薇。
Semaphore使用實(shí)例

Semaphore通常被用于控制資源的使用,例如胶征,一個(gè)數(shù)據(jù)庫(kù)服務(wù)器塞椎。在一些情況下,資源的大小被固定睛低,你應(yīng)該使用一個(gè)有界的Semaphore忱屑。在啟動(dòng)其他工作線程之前蹬敲,你的主線程首先初始化Semaphore:
[python] view plain copy
maxconnections = 5

...

pool_sema = BoundedSemaphore(value=maxconnections)
其它工作線程則在需要連接到服務(wù)器時(shí)調(diào)用Semaphore的請(qǐng)求和釋放方法:
[python] view plain copy
with pool_sema:
conn = connectdb()
try:
# ... use connection ...
finally:
conn.close()
有個(gè)有界的Semaphore可以減少程序出錯(cuò)的機(jī)會(huì),防止semaphore釋放的次數(shù)大于請(qǐng)求次數(shù)引發(fā)的問題莺戒。
Event對(duì)象

這是在線程間最簡(jiǎn)單的通信機(jī)制之一:一個(gè)線程通知一個(gè)事件,另一個(gè)線程等待通知并作出處理急波。
一個(gè)Event對(duì)象管理一個(gè)內(nèi)部標(biāo)志从铲,使用set()方法可以將其設(shè)置為True,使用clear()方法可以將其重設(shè)為False澄暮。wait()方法將阻塞直到標(biāo)志為True名段。

class threading.Event
該類實(shí)現(xiàn)事件對(duì)象。一個(gè)事件管理一個(gè)標(biāo)志泣懊,可以通過set()方法將其設(shè)置為True伸辟,通過clear()方法將其重設(shè)為False。wait()方法阻塞直到標(biāo)志為True馍刮。標(biāo)志初始為False信夫。
1)is_set()
當(dāng)且僅當(dāng)內(nèi)部標(biāo)志為True時(shí)返回True。
2)set()
設(shè)置標(biāo)志為True卡啰。所有等待的線程都將被喚醒静稻。一旦標(biāo)志為True,調(diào)用wait()的線程將不再阻塞匈辱。
3)clear()
重設(shè)標(biāo)志為False振湾。接下來,所有調(diào)用wait()的線程將阻塞直到set()被調(diào)用亡脸。
4)wait(timeout=None)
阻塞直到標(biāo)志被設(shè)置為True押搪。如果標(biāo)志已經(jīng)為True,則立即返回浅碾;否則大州,阻塞直到另一個(gè)線程調(diào)用set(),或者直到超時(shí)及穗。
當(dāng)timeout參數(shù)被指定且不為None摧茴,線程將僅等待timeout的秒數(shù)。
該方法當(dāng)標(biāo)志為True時(shí)返回True埂陆,當(dāng)超時(shí)時(shí)返回False苛白。
Timer對(duì)象

該類表示了一個(gè)定時(shí)器,表示一個(gè)行為在多少時(shí)間后被執(zhí)行焚虱。Timer是Thread的子類购裙,可以作為創(chuàng)建自定義線程的一個(gè)實(shí)例。
Timer通過調(diào)用start()方法啟動(dòng)鹃栽,通過調(diào)用cancel()方法停止(必須在行為被執(zhí)行之前)躏率。Timer執(zhí)行指定行為之間的等待時(shí)間并不是精確的,也就是說可能與用戶指定的間隔存在差異。
例如:
[python] view plain copy
def hello():
print("hello, world")

t = Timer(30.0, hello)
t.start() # 30秒后薇芝,"hello, world"將被打印
class threading.Timer(interval, function, args=None, kwargs=None)
創(chuàng)建一個(gè)Timer蓬抄,在interval秒之后,將使用參數(shù)args和kwargs作為參數(shù)執(zhí)行function夯到。如果args為None(默認(rèn))嚷缭,將使用空list。如果kwargs是None(默認(rèn))耍贾,則使用空字典阅爽。
1)cancel()
停止定時(shí)器,并且取消定時(shí)器的行為的執(zhí)行荐开。這僅當(dāng)定時(shí)器任然處理等待狀態(tài)時(shí)才有效付翁。
Barrier對(duì)象

柵欄提供了一個(gè)簡(jiǎn)單的同步基元,用于固定數(shù)量的線程需要等待彼此的場(chǎng)景晃听。嘗試通過柵欄的每個(gè)線程都會(huì)調(diào)用wait()方法百侧,然后阻塞直到所有的線程都調(diào)用了該方法,然后杂伟,所有線程同時(shí)被釋放移层。
柵欄能被重復(fù)使用任意多次,但必須是同等數(shù)量的線程赫粥。
下面是一個(gè)例子观话,一個(gè)同步客戶端和服務(wù)端線程的簡(jiǎn)單方法:
[python] view plain copy
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個(gè)線程創(chuàng)建一個(gè)柵欄對(duì)象,如果提供了action越平,則當(dāng)線程被釋放時(shí)频蛔,它將被線程中的一個(gè)調(diào)用。timeout表示wait()方法的默認(rèn)超時(shí)時(shí)間值秦叛。
1)wait(timeout=None)
通過柵欄晦溪。當(dāng)所有使用柵欄的線程都調(diào)用了該方法后,他們將被同時(shí)釋放挣跋。如果timeout被提供三圆,他優(yōu)先于類構(gòu)造器提供的timeout參數(shù)。
返回值是0到parties的整數(shù)避咆,每個(gè)線程都不同舟肉。這能用于選擇某個(gè)特定的線程做一些特定的操作,例如:
[python] view plain copy
i = barrier.wait()
if i == 0:
# Only one thread needs to print this
print("passed the barrier")
如果一個(gè)action被提供給了構(gòu)造器查库,線程中的其中一個(gè)將在被釋放時(shí)調(diào)用它路媚,如果調(diào)用拋出一個(gè)異常,則柵欄進(jìn)入損壞狀態(tài)樊销。
如果調(diào)用超時(shí)整慎,則柵欄進(jìn)入損壞狀態(tài)脏款。
如果柵欄處于損壞狀態(tài),或者有線程在等待時(shí)被重設(shè)了裤园,則該方法會(huì)拋出BrokenBarrierError異常撤师。
2)reset()
恢復(fù)柵欄到默認(rèn)狀態(tài)。任何處于等待中的線程將收到BrokenBarrierError異常比然。
注意這里需要額外的同步丈氓。如果一個(gè)柵欄被損壞,創(chuàng)建一個(gè)新的柵欄也許是更好的選擇强法。
3)abort()
放置柵欄到損壞狀態(tài)。這導(dǎo)致當(dāng)前處于等待的線程和未來對(duì)wait()的調(diào)用都會(huì)拋出BrokenBarrierError湾笛。通常使用該方法是為了避免死鎖饮怯。
使用一個(gè)超時(shí)時(shí)間應(yīng)該是更好的選擇。
4)parties
要求通過柵欄的線程的數(shù)量嚎研。
5)n_waiting
當(dāng)前處于等待中的線程數(shù)量蓖墅。
6)broken
柵欄是否處于損壞狀態(tài),如果是則為True临扮。

exception threading.BrokenBarrierError
該異常是RuntimeError的子類论矾,當(dāng)柵欄對(duì)象被重設(shè)或者損壞時(shí)被拋出。
在with語(yǔ)句中使用locks杆勇、conditions和semaphores

所有被該模塊提供的具有acquire()和release()方法的對(duì)象都能使用with語(yǔ)句管理贪壳。當(dāng)進(jìn)入阻塞狀態(tài)時(shí)acquire()方法將被調(diào)用,而當(dāng)推出阻塞狀態(tài)時(shí)release()方法將被調(diào)用蚜退。下面是具體的語(yǔ)法:
[python] view plain copy
with some_lock:
# do something...
等價(jià)于:
[python] view plain copy
some_lock.acquire()
try:
# do something...
finally:
some_lock.release()
當(dāng)前闰靴,Lock、RLock钻注、Condition蚂且、Semaphore和BoundedSemaphore都可以在with語(yǔ)句中管理。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末幅恋,一起剝皮案震驚了整個(gè)濱河市杏死,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌捆交,老刑警劉巖淑翼,帶你破解...
    沈念sama閱讀 218,682評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異零渐,居然都是意外死亡窒舟,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門诵盼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來惠豺,“玉大人银还,你說我怎么就攤上這事〗嗲剑” “怎么了蛹疯?”我有些...
    開封第一講書人閱讀 165,083評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)热监。 經(jīng)常有香客問我捺弦,道長(zhǎng),這世上最難降的妖魔是什么孝扛? 我笑而不...
    開封第一講書人閱讀 58,763評(píng)論 1 295
  • 正文 為了忘掉前任列吼,我火速辦了婚禮,結(jié)果婚禮上苦始,老公的妹妹穿的比我還像新娘寞钥。我一直安慰自己,他們只是感情好陌选,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評(píng)論 6 392
  • 文/花漫 我一把揭開白布理郑。 她就那樣靜靜地躺著,像睡著了一般咨油。 火紅的嫁衣襯著肌膚如雪您炉。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,624評(píng)論 1 305
  • 那天役电,我揣著相機(jī)與錄音赚爵,去河邊找鬼。 笑死宴霸,一個(gè)胖子當(dāng)著我的面吹牛囱晴,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播瓢谢,決...
    沈念sama閱讀 40,358評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼畸写,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了氓扛?” 一聲冷哼從身側(cè)響起枯芬,我...
    開封第一講書人閱讀 39,261評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎采郎,沒想到半個(gè)月后千所,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,722評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蒜埋,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年淫痰,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片整份。...
    茶點(diǎn)故事閱讀 40,030評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡待错,死狀恐怖籽孙,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情火俄,我是刑警寧澤犯建,帶...
    沈念sama閱讀 35,737評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站瓜客,受9級(jí)特大地震影響适瓦,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜谱仪,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評(píng)論 3 330
  • 文/蒙蒙 一玻熙、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧疯攒,春花似錦揭芍、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)肌毅。三九已至筷转,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間悬而,已是汗流浹背呜舒。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留笨奠,地道東北人袭蝗。 一個(gè)月前我還...
    沈念sama閱讀 48,237評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像般婆,于是被迫代替她去往敵國(guó)和親到腥。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 線程狀態(tài)新建蔚袍,就緒乡范,運(yùn)行,阻塞啤咽,死亡晋辆。 線程同步多線程可以同時(shí)運(yùn)行多個(gè)任務(wù),線程需要共享數(shù)據(jù)的時(shí)候宇整,可能出現(xiàn)數(shù)據(jù)不...
    KevinCool閱讀 806評(píng)論 0 0
  • 進(jìn)程與線程的區(qū)別 現(xiàn)在瓶佳,多核CPU已經(jīng)非常普及了,但是鳞青,即使過去的單核CPU霸饲,也可以執(zhí)行多任務(wù)为朋。由于CPU執(zhí)行代碼...
    蘇糊閱讀 767評(píng)論 0 2
  • 本文主要講了java中多線程的使用方法、線程同步贴彼、線程數(shù)據(jù)傳遞潜腻、線程狀態(tài)及相應(yīng)的一些線程函數(shù)用法、概述等器仗。 首先講...
    李欣陽(yáng)閱讀 2,456評(píng)論 1 15
  • 五百世時(shí)融涣,我們是君臣 你騎馬過南山,寶劍如霜精钮,梨花似雪 燧木取火后威鹿,我為你研墨穿簡(jiǎn) 燈燭搖曳,恰如黑夜迷茫的孤眼 ...
    南風(fēng)認(rèn)識(shí)我閱讀 251評(píng)論 5 8
  • 導(dǎo)語(yǔ):裝修風(fēng)格有很多種轨香,每個(gè)人的喜好都不盡相同忽你,經(jīng)常會(huì)有業(yè)主糾結(jié)說到底哪種才是最適合自己家的。裝修風(fēng)格的確立讓設(shè)計(jì)...
    變更丶閱讀 2,045評(píng)論 0 11