文章目錄
- Thread.ident
- Thread.join([timeout])
- Condition.wait([timeout]):
- Condition.notify():
- Event.wait([timeout])
- Event.set()
- Event.clear()
- Event.isSet()
- threading.enumerate()
- threading.settrace(func)
- threading.setprofile(func)
上一篇
介紹了thread模組,今天來學習Python中另一個操作線程的模組:threading。threading通過對thread模組進行二次封裝,提供了更方便的API來操作線程。今天內容比較多,閑話少說,現在就開始切入正題!
threading.Thread
Thread 是threading模組中最重要的類之一,可以使用它來建立線程。有兩種方式來建立線程:一種是通過繼承Thread類,重寫它的run方法;另一種是建立一個threading.Thread對象,在它的初始化函數(__init__)中將可調用對象作為參數傳入。下面分別舉例說明。先來看看通過繼承threading.Thread類來建立線程的例子:
#coding=gbk<br />import threading, time, random<br />count = 0<br />class Counter(threading.Thread):<br /> def __init__(self, lock, threadName):<br /> '''@summary: 初始化對象。</p><p> @param lock: 瑣對象。<br /> @param threadName: 線程名稱。<br /> '''<br /> super(Counter, self).__init__(name = threadName) #注意:一定要顯式的調用父類的初始<br />化函數。<br /> self.lock = lock</p><p> def run(self):<br /> '''@summary: 重寫父類run方法,線上程啟動後執行該方法內的代碼。<br /> '''<br /> global count<br /> self.lock.acquire()<br /> for i in xrange(10000):<br /> count = count + 1<br /> self.lock.release()<br />lock = threading.Lock()<br />for i in range(5):<br /> Counter(lock, "thread-" + str(i)).start()<br />time.sleep(2)#確保線程都執行完畢<br />print count
在代碼中,我們建立了一個Counter類,它繼承了threading.Thread。初始化函數接收兩個參數,一個是瑣對象,另一個是線程的名稱。在Counter中,重寫了從父類繼承的run方法,run方法將一個全域變數逐一的增加10000。在接下來的代碼中,建立了五個Counter對象,分別調用其start方法。最後列印結果。這裡要說明一下run方法
和start方法:
它們都是從Thread繼承而來的,run()方法將線上程開啟後執行,可以把相關的邏輯寫到run方法中(通常把run方法稱為活動[Activity]。);start()方法用於啟動線程。
再看看另外一種建立線程的方法:
import threading, time, random<br />count = 0<br />lock = threading.Lock()<br />def doAdd():<br /> '''@summary: 將全域變數count 逐一的增加10000。<br /> '''<br /> global count, lock<br /> lock.acquire()<br /> for i in xrange(10000):<br /> count = count + 1<br /> lock.release()<br />for i in range(5):<br /> threading.Thread(target = doAdd, args = (), name = 'thread-' + str(i)).start()<br />time.sleep(2)#確保線程都執行完畢<br />print count
在這段代碼中,我們定義了方法doAdd,它將全域變數count 逐一的增加10000。然後建立了5個Thread對象,把函數對象doAdd 作為參數傳給它的初始化函數,再調用Thread對象的start方法,線程啟動後將執行doAdd函數。這裡有必要介紹一下threading.Thread類的初始化函數原型:
def __init__(self, group=None, target=None, name=None, args=(), kwargs={})
參數group是預留的,用於將來擴充;
參數target是一個可調用對象(也稱為活動[activity]),線上程啟動後執行;
參數name是線程的名字。預設值為“Thread-N“,N是一個數字。
參數args和kwargs分別表示調用target時的參數列表和關鍵字參數。
Thread類還定義了以下常用方法與屬性:
Thread.getName()
Thread.setName()
Thread.name
用於擷取和設定線程的名稱。
Thread.ident
擷取線程的標識符。線程標識符是一個非零整數,只有在調用了start()方法之後該屬性才有效,否則它只返回None。
Thread.is_alive()
Thread.isAlive()
判斷線程是否是啟用的(alive)。從調用start()方法啟動線程,到run()方法執行完畢或遇到未處理異常而中斷 這段時間內,線程是啟用的。
Thread.join([timeout])
調用Thread.join將會使主調線程堵塞,直到被調用線程運行結束或逾時。參數timeout是一個數實值型別,表示逾時時間,如果未提供該參數,那麼主調線程將一直堵塞到被調線程結束。下面舉個例子說明join()的使用:
import threading, time<br />def doWaiting():<br /> print 'start waiting:', time.strftime('%H:%M:%S')<br /> time.sleep(3)<br /> print 'stop waiting', time.strftime('%H:%M:%S')<br />thread1 = threading.Thread(target = doWaiting)<br />thread1.start()<br />time.sleep(1) #確保線程thread1已經啟動<br />print 'start join'<br />thread1.join()#將一直堵塞,直到thread1運行結束。<br />print 'end join'
threading.RLock和threading.Lock
在threading模組中,定義兩種類型的瑣:threading.Lock和threading.RLock。它們之間有一點細微的區別,通過比較下面兩段代碼來說明:
import threading<br />lock = threading.Lock()#Lock對象<br />lock.acquire()<br />lock.acquire() #產生了死瑣。<br />lock.release()<br />lock.release()
import threading<br />rLock = threading.RLock() #RLock對象<br />rLock.acquire()<br />rLock.acquire()#在同一線程內,程式不會堵塞。<br />rLock.release()<br />rLock.release()
這兩種瑣的主要區別是:RLock允許在同一線程中被多次acquire。而Lock卻不允許這種情況。注意:如果使用RLock,那麼acquire和release必須成對出現,即調用了n次acquire,必須調用n次的release才能真正釋放所佔用的瑣。
threading.Condition
可以把Condiftion理解為一把進階的瑣,它提供了比Lock, RLock更進階的功能,允許我們能夠控制複雜的線程同步問題。threadiong.Condition在內部維護一個瑣對象(預設是RLock),可以在建立Condigtion對象的時候把瑣對象作為參數傳入。Condition也提供了acquire, release方法,其含義與瑣的acquire, release方法一致,其實它只是簡單的調用內部瑣對象的對應的方法而已。Condition還提供了如下方法(特別要注意:這些方法只有在佔用瑣(acquire)之後才能調用,否則將會報RuntimeError異常。):
Condition.wait([timeout]):
wait方法釋放內部所佔用的瑣,同時線程被掛起,直至接收到通知被喚醒或逾時(如果提供了timeout參數的話)。當線程被喚醒並重新佔有瑣的時候,程式才會繼續執行下去。
Condition.notify():
喚醒一個掛起的線程(如果存在掛起的線程)。注意:notify()方法不會釋放所佔用的瑣。
Condition.notify_all()
Condition.notifyAll()
喚醒所有掛起的線程(如果存在掛起的線程)。注意:這些方法不會釋放所佔用的瑣。
現在寫個捉迷藏的遊戲來具體介紹threading.Condition的基本使用。假設這個遊戲由兩個人來玩,一個藏(Hider),一個找(Seeker)。遊戲的規則如下:1. 遊戲開始之後,Seeker先把自己眼睛蒙上,蒙上眼睛後,就通知Hider;2. Hider接收通知後開始找地方將自己藏起來,藏好之後,再通知Seeker可以找了; 3. Seeker接收到通知之後,就開始找Hider。Hider和Seeker都是獨立的個體,在程式中用兩個獨立的線程來表示,在遊戲過程中,兩者之間的行為有一定的時序關係,我們通過Condition來控制這種時序關係。
#---- Condition<br />#---- 捉迷藏的遊戲<br />import threading, time<br />class Hider(threading.Thread):<br /> def __init__(self, cond, name):<br /> super(Hider, self).__init__()<br /> self.cond = cond<br /> self.name = name</p><p> def run(self):<br /> time.sleep(1) #確保先運行Seeker中的方法 </p><p> self.cond.acquire() #b<br /> print self.name + ': 我已經把眼睛蒙上了'<br /> self.cond.notify()<br /> self.cond.wait() #c<br /> #f<br /> print self.name + ': 我找到你了 ~_~'<br /> self.cond.notify()<br /> self.cond.release()<br /> #g<br /> print self.name + ': 我贏了' #h</p><p>class Seeker(threading.Thread):<br /> def __init__(self, cond, name):<br /> super(Seeker, self).__init__()<br /> self.cond = cond<br /> self.name = name<br /> def run(self):<br /> self.cond.acquire()<br /> self.cond.wait() #a #釋放對瑣的佔用,同時線程掛起在這裡,直到被notify並重新占<br />有瑣。<br /> #d<br /> print self.name + ': 我已經藏好了,你快來找我吧'<br /> self.cond.notify()<br /> self.cond.wait() #e<br /> #h<br /> self.cond.release()<br /> print self.name + ': 被你找到了,哎~~~'</p><p>cond = threading.Condition()<br />seeker = Seeker(cond, 'seeker')<br />hider = Hider(cond, 'hider')<br />seeker.start()<br />hider.start()
threading.Event
Event實現與Condition類似的功能,不過比Condition簡單一點。它通過維護內部的標識符來實現線程間的同步問題。(threading.Event和.NET中的System.Threading.ManualResetEvent類實現同樣的功能。)
Event.wait([timeout])
堵塞線程,直到Event對象內部標識位被設為True或逾時(如果提供了參數timeout)。
Event.set()
將標識位設為Ture
Event.clear()
將標識伴設為False。
Event.isSet()
判斷標識位是否為Ture。
下面使用Event來實現捉迷藏的遊戲(可能用Event來實現不是很形象)
#---- Event<br />#---- 捉迷藏的遊戲<br />import threading, time<br />class Hider(threading.Thread):<br /> def __init__(self, cond, name):<br /> super(Hider, self).__init__()<br /> self.cond = cond<br /> self.name = name</p><p> def run(self):<br /> time.sleep(1) #確保先運行Seeker中的方法 </p><p> print self.name + ': 我已經把眼睛蒙上了'</p><p> self.cond.set()</p><p> time.sleep(1) </p><p> self.cond.wait()<br /> print self.name + ': 我找到你了 ~_~'</p><p> self.cond.set()</p><p> print self.name + ': 我贏了'</p><p>class Seeker(threading.Thread):<br /> def __init__(self, cond, name):<br /> super(Seeker, self).__init__()<br /> self.cond = cond<br /> self.name = name<br /> def run(self):<br /> self.cond.wait()</p><p> print self.name + ': 我已經藏好了,你快來找我吧'<br /> self.cond.set()</p><p> time.sleep(1)<br /> self.cond.wait()</p><p> print self.name + ': 被你找到了,哎~~~'</p><p>cond = threading.Event()<br />seeker = Seeker(cond, 'seeker')<br />hider = Hider(cond, 'hider')<br />seeker.start()<br />hider.start()
threading.Timer
threading.Timer是threading.Thread的子類,可以在指定時間間隔後執行某個操作。下面是Python手冊上提供的一個例子:
def hello():<br /> print "hello, world"<br />t = Timer(3, hello)<br />t.start() # 3秒鐘之後執行hello函數。
threading模組中還有一些常用的方法沒有介紹:
threading.active_count()
threading.activeCount()
擷取當前活動的(alive)線程的個數。
threading.current_thread()
threading.currentThread()
擷取當前的線程對象(Thread object)。
threading.enumerate()
擷取當前所有活動線程的列表。
threading.settrace(func)
設定一個跟蹤函數,用於在run()執行之前被調用。
threading.setprofile(func)
設定一個跟蹤函數,用於在run()執行完畢之後調用。
threading模組的內容很多,一篇文章很難寫全,更多關於threading模組的資訊,請查詢Python手冊 threading
模組。