Python中線(xiàn)程編程之threading模塊的使用詳解
threading.Thread
Thread 是threading模塊中最重要的類(lèi)之一,可以使用它來(lái)創(chuàng)建線(xiàn)程。有兩種方式來(lái)創(chuàng)建線(xiàn)程:一種是通過(guò)繼承Thread類(lèi),重寫(xiě)它的run方法;另一種是創(chuàng)建一個(gè)threading.Thread對(duì)象,在它的初始化函數(shù)(__init__)中將可調(diào)用對(duì)象作為參數(shù)傳入。下面分別舉例說(shuō)明。先來(lái)看看通過(guò)繼承threading.Thread類(lèi)來(lái)創(chuàng)建線(xiàn)程的例子:
#coding=gbk import threading, time, random count = 0 class Counter(threading.Thread): def __init__(self, lock, threadName): '''@summary: 初始化對(duì)象。 @param lock: 瑣對(duì)象。 @param threadName: 線(xiàn)程名稱(chēng)。 ''' super(Counter, self).__init__(name = threadName) #注意:一定要顯式的調(diào)用父類(lèi)的初始 化函數(shù)。 self.lock = lock def run(self): '''@summary: 重寫(xiě)父類(lèi)run方法,在線(xiàn)程啟動(dòng)后執(zhí)行該方法內(nèi)的代碼。 ''' global count self.lock.acquire() for i in xrange(10000): count = count + 1 self.lock.release() lock = threading.Lock() for i in range(5): Counter(lock, "thread-" + str(i)).start() time.sleep(2) #確保線(xiàn)程都執(zhí)行完畢 print count
在代碼中,我們創(chuàng)建了一個(gè)Counter類(lèi),它繼承了threading.Thread。初始化函數(shù)接收兩個(gè)參數(shù),一個(gè)是瑣對(duì)象,另一個(gè)是線(xiàn)程的名稱(chēng)。在Counter中,重寫(xiě)了從父類(lèi)繼承的run方法,run方法將一個(gè)全局變量逐一的增加10000。在接下來(lái)的代碼中,創(chuàng)建了五個(gè)Counter對(duì)象,分別調(diào)用其start方法。最后打印結(jié)果。這里要說(shuō)明一下run方法 和start方法: 它們都是從Thread繼承而來(lái)的,run()方法將在線(xiàn)程開(kāi)啟后執(zhí)行,可以把相關(guān)的邏輯寫(xiě)到run方法中(通常把run方法稱(chēng)為活動(dòng)[Activity]。);start()方法用于啟動(dòng)線(xiàn)程。
再看看另外一種創(chuàng)建線(xiàn)程的方法:
import threading, time, random count = 0 lock = threading.Lock() def doAdd(): '''@summary: 將全局變量count 逐一的增加10000。 ''' global count, lock lock.acquire() for i in xrange(10000): count = count + 1 lock.release() for i in range(5): threading.Thread(target = doAdd, args = (), name = 'thread-' + str(i)).start() time.sleep(2) #確保線(xiàn)程都執(zhí)行完畢 print count
在這段代碼中,我們定義了方法doAdd,它將全局變量count 逐一的增加10000。然后創(chuàng)建了5個(gè)Thread對(duì)象,把函數(shù)對(duì)象doAdd 作為參數(shù)傳給它的初始化函數(shù),再調(diào)用Thread對(duì)象的start方法,線(xiàn)程啟動(dòng)后將執(zhí)行doAdd函數(shù)。這里有必要介紹一下threading.Thread類(lèi)的初始化函數(shù)原型:
- 參數(shù)group是預(yù)留的,用于將來(lái)擴(kuò)展;
- 參數(shù)target是一個(gè)可調(diào)用對(duì)象(也稱(chēng)為活動(dòng)[activity]),在線(xiàn)程啟動(dòng)后執(zhí)行;
- 參數(shù)name是線(xiàn)程的名字。默認(rèn)值為“Thread-N“,N是一個(gè)數(shù)字。
- 參數(shù)args和kwargs分別表示調(diào)用target時(shí)的參數(shù)列表和關(guān)鍵字參數(shù)。
def __init__(self, group=None, target=None, name=None, args=(), kwargs={})
Thread類(lèi)還定義了以下常用方法與屬性:
Thread.getName()
Thread.setName()
Thread.name
用于獲取和設(shè)置線(xiàn)程的名稱(chēng)。
Thread.ident
獲取線(xiàn)程的標(biāo)識(shí)符。線(xiàn)程標(biāo)識(shí)符是一個(gè)非零整數(shù),只有在調(diào)用了start()方法之后該屬性才有效,否則它只返回None。
Thread.is_alive()
Thread.isAlive()
判斷線(xiàn)程是否是激活的(alive)。從調(diào)用start()方法啟動(dòng)線(xiàn)程,到run()方法執(zhí)行完畢或遇到未處理異常而中斷 這段時(shí)間內(nèi),線(xiàn)程是激活的。
Thread.join([timeout])
調(diào)用Thread.join將會(huì)使主調(diào)線(xiàn)程堵塞,直到被調(diào)用線(xiàn)程運(yùn)行結(jié)束或超時(shí)。參數(shù)timeout是一個(gè)數(shù)值類(lèi)型,表示超時(shí)時(shí)間,如果未提供該參數(shù),那么主調(diào)線(xiàn)程將一直堵塞到被調(diào)線(xiàn)程結(jié)束。下面舉個(gè)例子說(shuō)明join()的使用:
import threading, time def doWaiting(): print 'start waiting:', time.strftime('%H:%M:%S') time.sleep(3) print 'stop waiting', time.strftime('%H:%M:%S') thread1 = threading.Thread(target = doWaiting) thread1.start() time.sleep(1) #確保線(xiàn)程thread1已經(jīng)啟動(dòng) print 'start join' thread1.join() #將一直堵塞,直到thread1運(yùn)行結(jié)束。 print 'end join' threading.RLock和threading.Lock
在threading模塊中,定義兩種類(lèi)型的瑣:threading.Lock和threading.RLock。它們之間有一點(diǎn)細(xì)微的區(qū)別,通過(guò)比較下面兩段代碼來(lái)說(shuō)明:
import threading lock = threading.Lock() #Lock對(duì)象 lock.acquire() lock.acquire() #產(chǎn)生了死瑣。 lock.release() lock.release() import threading rLock = threading.RLock() #RLock對(duì)象 rLock.acquire() rLock.acquire() #在同一線(xiàn)程內(nèi),程序不會(huì)堵塞。 rLock.release() rLock.release()
這兩種瑣的主要區(qū)別是:RLock允許在同一線(xiàn)程中被多次acquire。而Lock卻不允許這種情況。注意:如果使用RLock,那么acquire和release必須成對(duì)出現(xiàn),即調(diào)用了n次acquire,必須調(diào)用n次的release才能真正釋放所占用的瑣。
threading.Condition
可以把Condiftion理解為一把高級(jí)的瑣,它提供了比Lock, RLock更高級(jí)的功能,允許我們能夠控制復(fù)雜的線(xiàn)程同步問(wèn)題。threadiong.Condition在內(nèi)部維護(hù)一個(gè)瑣對(duì)象(默認(rèn)是RLock),可以在創(chuàng)建Condigtion對(duì)象的時(shí)候把瑣對(duì)象作為參數(shù)傳入。Condition也提供了acquire, release方法,其含義與瑣的acquire, release方法一致,其實(shí)它只是簡(jiǎn)單的調(diào)用內(nèi)部瑣對(duì)象的對(duì)應(yīng)的方法而已。Condition還提供了如下方法(特別要注意:這些方法只有在占用瑣(acquire)之后才能調(diào)用,否則將會(huì)報(bào)RuntimeError異常。):
Condition.wait([timeout]):
wait方法釋放內(nèi)部所占用的瑣,同時(shí)線(xiàn)程被掛起,直至接收到通知被喚醒或超時(shí)(如果提供了timeout參數(shù)的話(huà))。當(dāng)線(xiàn)程被喚醒并重新占有瑣的時(shí)候,程序才會(huì)繼續(xù)執(zhí)行下去。
Condition.notify():
喚醒一個(gè)掛起的線(xiàn)程(如果存在掛起的線(xiàn)程)。注意:notify()方法不會(huì)釋放所占用的瑣。
Condition.notify_all()
Condition.notifyAll()
喚醒所有掛起的線(xiàn)程(如果存在掛起的線(xiàn)程)。注意:這些方法不會(huì)釋放所占用的瑣。
現(xiàn)在寫(xiě)個(gè)捉迷藏的游戲來(lái)具體介紹threading.Condition的基本使用。假設(shè)這個(gè)游戲由兩個(gè)人來(lái)玩,一個(gè)藏(Hider),一個(gè)找(Seeker)。游戲的規(guī)則如下:1. 游戲開(kāi)始之后,Seeker先把自己眼睛蒙上,蒙上眼睛后,就通知Hider;2. Hider接收通知后開(kāi)始找地方將自己藏起來(lái),藏好之后,再通知Seeker可以找了; 3. Seeker接收到通知之后,就開(kāi)始找Hider。Hider和Seeker都是獨(dú)立的個(gè)體,在程序中用兩個(gè)獨(dú)立的線(xiàn)程來(lái)表示,在游戲過(guò)程中,兩者之間的行為有一定的時(shí)序關(guān)系,我們通過(guò)Condition來(lái)控制這種時(shí)序關(guān)系。
#---- Condition #---- 捉迷藏的游戲 import threading, time class Hider(threading.Thread): def __init__(self, cond, name): super(Hider, self).__init__() self.cond = cond self.name = name def run(self): time.sleep(1) #確保先運(yùn)行Seeker中的方法 self.cond.acquire() #b print self.name + ': 我已經(jīng)把眼睛蒙上了' self.cond.notify() self.cond.wait() #c #f print self.name + ': 我找到你了 ~_~' self.cond.notify() self.cond.release() #g print self.name + ': 我贏(yíng)了' #h class Seeker(threading.Thread): def __init__(self, cond, name): super(Seeker, self).__init__() self.cond = cond self.name = name def run(self): self.cond.acquire() self.cond.wait() #a #釋放對(duì)瑣的占用,同時(shí)線(xiàn)程掛起在這里,直到被notify并重新占 有瑣。 #d print self.name + ': 我已經(jīng)藏好了,你快來(lái)找我吧' self.cond.notify() self.cond.wait() #e #h self.cond.release() print self.name + ': 被你找到了,哎~~~' cond = threading.Condition() seeker = Seeker(cond, 'seeker') hider = Hider(cond, 'hider') seeker.start() hider.start() threading.Event
Event實(shí)現(xiàn)與Condition類(lèi)似的功能,不過(guò)比Condition簡(jiǎn)單一點(diǎn)。它通過(guò)維護(hù)內(nèi)部的標(biāo)識(shí)符來(lái)實(shí)現(xiàn)線(xiàn)程間的同步問(wèn)題。(threading.Event和.NET中的System.Threading.ManualResetEvent類(lèi)實(shí)現(xiàn)同樣的功能。)
Event.wait([timeout])
堵塞線(xiàn)程,直到Event對(duì)象內(nèi)部標(biāo)識(shí)位被設(shè)為T(mén)rue或超時(shí)(如果提供了參數(shù)timeout)。
Event.set()
將標(biāo)識(shí)位設(shè)為T(mén)ure
Event.clear()
將標(biāo)識(shí)伴設(shè)為False。
Event.isSet()
判斷標(biāo)識(shí)位是否為T(mén)ure。
下面使用Event來(lái)實(shí)現(xiàn)捉迷藏的游戲(可能用Event來(lái)實(shí)現(xiàn)不是很形象)
#---- Event #---- 捉迷藏的游戲 import threading, time class Hider(threading.Thread): def __init__(self, cond, name): super(Hider, self).__init__() self.cond = cond self.name = name def run(self): time.sleep(1) #確保先運(yùn)行Seeker中的方法 print self.name + ': 我已經(jīng)把眼睛蒙上了' self.cond.set() time.sleep(1) self.cond.wait() print self.name + ': 我找到你了 ~_~' self.cond.set() print self.name + ': 我贏(yíng)了' class Seeker(threading.Thread): def __init__(self, cond, name): super(Seeker, self).__init__() self.cond = cond self.name = name def run(self): self.cond.wait() print self.name + ': 我已經(jīng)藏好了,你快來(lái)找我吧' self.cond.set() time.sleep(1) self.cond.wait() print self.name + ': 被你找到了,哎~~~' cond = threading.Event() seeker = Seeker(cond, 'seeker') hider = Hider(cond, 'hider') seeker.start() hider.start() threading.Timer
threading.Timer是threading.Thread的子類(lèi),可以在指定時(shí)間間隔后執(zhí)行某個(gè)操作。下面是Python手冊(cè)上提供的一個(gè)例子:
def hello(): print "hello, world" t = Timer(3, hello) t.start() # 3秒鐘之后執(zhí)行hello函數(shù)。
threading模塊中還有一些常用的方法沒(méi)有介紹:
threading.active_count()
threading.activeCount()
獲取當(dāng)前活動(dòng)的(alive)線(xiàn)程的個(gè)數(shù)。
threading.current_thread()
threading.currentThread()
獲取當(dāng)前的線(xiàn)程對(duì)象(Thread object)。
threading.enumerate()
獲取當(dāng)前所有活動(dòng)線(xiàn)程的列表。
threading.settrace(func)
設(shè)置一個(gè)跟蹤函數(shù),用于在run()執(zhí)行之前被調(diào)用。
threading.setprofile(func)
設(shè)置一個(gè)跟蹤函數(shù),用于在run()執(zhí)行完畢之后調(diào)用。
threading模塊的內(nèi)容很多,一篇文章很難寫(xiě)全,更多關(guān)于threading模塊的信息,請(qǐng)查詢(xún)Python手冊(cè) threading模塊。
相關(guān)文章
- 這篇文章主要介紹了python3連接MySQL8.0的兩種方式,本文通過(guò)多種方式給大家介紹的非常詳細(xì),代碼附有文字注釋?zhuān)枰呐笥芽梢詤⒖枷?/div> 2020-02-02
python多進(jìn)程提取處理大量文本的關(guān)鍵詞方法
今天小編就為大家分享一篇python多進(jìn)程提取處理大量文本的關(guān)鍵詞方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-06-06解決django無(wú)法訪(fǎng)問(wèn)本地static文件(js,css,img)網(wǎng)頁(yè)里js,cs都加載不了
這篇文章主要介紹了解決django無(wú)法訪(fǎng)問(wèn)本地static文件(js,css,img)網(wǎng)頁(yè)里js,cs都加載不了的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-04-04使用Python的pencolor函數(shù)實(shí)現(xiàn)漸變色功能
這篇文章主要介紹了使用Python的pencolor函數(shù)實(shí)現(xiàn)漸變色功能,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-03-03一波神奇的Python語(yǔ)句、函數(shù)與方法的使用技巧總結(jié)
這篇文章主要介紹了一波神奇的Python函數(shù)與方法的使用技巧總結(jié),包括裝飾器和with語(yǔ)句等的不常見(jiàn)用法,需要的朋友可以參考下2015-12-12使用 Python 列出串口的實(shí)現(xiàn)方法
有時(shí)在編程時(shí),我們需要獲取有關(guān)系統(tǒng)中可用通信端口的信息, 我們將討論如何使用 Python 來(lái)做到這一點(diǎn),將討論使用串口或 com 端口的通信, 我們將深入探索 Python 包,以幫助我們獲得系統(tǒng)的可用通信端口,感興趣的朋友一起看看吧2023-08-08Python的Flask開(kāi)發(fā)框架簡(jiǎn)單上手筆記
這篇文章主要介紹了Python的Flask開(kāi)發(fā)框架的入門(mén)知識(shí)整理,Flask是一款極輕的Python web開(kāi)發(fā)框架,需要的朋友可以參考下2015-11-11最新評(píng)論