之前我們已經學會如何在代碼塊中創建新的線程去執行我們要同步執行的多個任務,但是線程的世界遠不止如此。接下來,我們要介紹的是整個threading模塊。threading基於Java的線程模型設計。鎖(Lock)和條件變量(Condition)在Java中是對象的基本行為(每一個對象都自帶了鎖和條件變量),而在Python中則是獨立的對象,所以python的threading模塊中還提供了Lock,Rlock,Condition,Event等常用類,它們在python中是獨立於Tread模塊的,但是卻與線程緊密相關,不可分割。
需要注意的是:python的線程中沒有優先級、線程組,也不能被停止、暫停、恢復、中斷,線程只能隨着線程中的代碼執行完畢而被銷毀。查了n多資料之后終於接受了以上事實,個人覺得這是python的一個坑,導致了我在實現線程池的時候無法停止已經注入了方法且執行超時的線程。
threading模塊提供的類:
Thread, Lock, Rlock, Condition, [Bounded]Semaphore, Event, Timer, local.
threading 模塊提供的常用方法:
threading.currentThread(): 返回當前的線程變量。
threading.enumerate(): 返回一個包含正在運行的線程的list。正在運行指線程啟動后、結束前,不包括啟動前和終止后的線程。
threading.activeCount(): 返回正在運行的線程數量,與len(threading.enumerate())有相同的結果。
threading模塊常用類詳解
Thread類:我們使用Thread類來創建新的線程
- start 線程准備就緒,等待CPU調度
- setName 為線程設置名稱
- getName 獲取線程名稱
- setDaemon 設置為后台線程或前台線程(默認)
如果是后台線程,主線程執行過程中,后台線程也在進行,主線程執行完畢后,后台線程不論成功與否,均停止
如果是前台線程,主線程執行過程中,前台線程也在進行,主線程執行完畢后,等待前台線程也執行完成后,程序停止 - join 逐個執行每個線程,執行完畢后繼續往下執行,該方法是有高級用法的,代碼在下面
- run 線程被cpu調度后執行Thread類對象的run方法

1 import time 2 import threading 3 4 def printNum(a): 5 print 'num:',a 6 time.sleep(1) 7 8 def ThreadTest(i): 9 return threading.Thread(target=printNum, args=(999,)) 10 11 thread_arr = [] 12 for i in range(10): 13 t = ThreadTest(i) 14 thread_arr.append(t) 15 16 for t in thread_arr: 17 t.start() 18 19 for t in thread_arr: 20 t.join() 21 22 print 'finished'
Lock類和Rlock類:由於線程之間隨機調度:某線程可能在執行n條后,CPU接着執行其他線程。為了多個線程同時操作一個內存中的資源時不產生混亂,我們使用鎖
- acquire 給線程上鎖
- release 給線程解鎖
無論是lock還是rlock,提供的方法都非常簡單,acquire和release。但是rlock和lock的區別是什么呢?RLock允許在同一線程中被多次acquire。而Lock卻不允許這種情況。注意:如果使用RLock,那么acquire和release必須成對出現,即調用了n次acquire,必須調用n次的release才能真正釋放所占用的鎖。

1 #!/usr/bin/env python 2 #-*-coding:utf-8-*- 3 __author__ = 'Eva_J' 4 5 import threading 6 lock = threading.Lock() #Lock對象 7 lock.acquire() 8 lock.acquire() #產生了死鎖。 9 lock.release() 10 lock.release() 11 12 13 import threading 14 rLock = threading.RLock() #RLock對象 15 rLock.acquire() 16 rLock.acquire() #在同一線程內,程序不會堵塞。 17 rLock.release() 18 rLock.release()
Condition類:條件變量對象能讓一個線程停下來,等待其它線程滿足了某個“條件”。如,狀態的改變或值的改變。
- acquire 給線程上鎖
- wait wait方法釋放當前線程占用的鎖,同時掛起線程,直至被喚醒或超時(需timeout參數)。當線程被喚醒並重新占有鎖的時候,程序才會繼續執行下去。
- notify 喚醒一個掛起的線程(如果存在掛起的線程)。注:notify()方法不會釋放所占用的鎖。
- notifyall 調用這個方法將通知等待池中所有線程,這些線程都將進入鎖定池嘗試獲得鎖定。此方法不會釋放鎖定。使用前線程必須已獲得鎖定,否則將拋出異常。
比較經典的例子是下面這個生產者與消費者的例子,這個例子網上一搜到處都是,這里簡單解釋一下這段代碼的意義,代碼中寫了兩個類,Consumer和Producer,分別繼承了Thread類,我們分別初始化這兩個類獲得了c和p對象,並啟動這兩個線程。則這兩個線程去執行run方法(這里與Thread類內部的調度有關),定義了producer全局變量和condition對象為全局變量,當producer不大於1時,消費者線程被condition對象阻塞,不能繼續消費(這里是不再遞減),當producer不小於10時,生產者線程被condition對象阻塞,不再生產(這里是不再累加),代碼在下面,拿去執行,斷點一下就明白了。

1 #!/usr/bin/env python 2 #-*-coding:utf-8-*- 3 __author__ = 'Eva_J' 4 5 import threading 6 import time 7 8 condition = threading.Condition() 9 products = 0 10 11 class Producer(threading.Thread): 12 def __init__(self): 13 threading.Thread.__init__(self) 14 15 def run(self): 16 global condition, products 17 while True: 18 if condition.acquire(): 19 if products < 10: 20 products += 1; 21 print "Producer(%s):deliver one, now products:%s" %(self.name, products) 22 condition.notify() 23 else: 24 print "Producer(%s):already 10, stop deliver, now products:%s" %(self.name, products) 25 condition.wait(); 26 condition.release() 27 time.sleep(2) 28 29 class Consumer(threading.Thread): 30 def __init__(self): 31 threading.Thread.__init__(self) 32 33 def run(self): 34 global condition, products 35 while True: 36 if condition.acquire(): 37 if products > 1: 38 products -= 1 39 print "Consumer(%s):consume one, now products:%s" %(self.name, products) 40 condition.notify() 41 else: 42 print "Consumer(%s):only 1, stop consume, products:%s" %(self.name, products) 43 condition.wait(); 44 condition.release() 45 time.sleep(2) 46 47 if __name__ == "__main__": 48 for p in range(0, 2): 49 p = Producer() 50 p.start() 51 52 for c in range(0, 10): 53 c = Consumer() 54 c.start()
Event類:通用的條件變量。多個線程可以等待某個事件的發生,在事件發生后,所有的線程都會被激活。
- event.wait(timeout) 當Flag為‘False’時,線程將被阻塞
- clear 將“Flag”設置為False
- set 將“Flag”設置為True
- is_set 返回當前‘Flag’
這是一個比較關鍵的類,我在寫線程池的時候看到python的threadpool模塊也用到了。它的意義在於可以控制屬於同一個線程類的多個實例化對象,讓他們同時阻塞或者執行。配合隊列來實現一個線程池非常好用。在接下里的博客中我們還要繼續介紹。先放一個小例子在這里練練手,了解一下event的用法。

1 #!/usr/bin/env python 2 #-*-coding:utf-8-*- 3 __author__ = 'Eva_J' 4 5 import threading 6 import time 7 8 event = threading.Event() 9 10 def func(): 11 # 等待事件,進入等待阻塞狀態 12 print '%s wait for event...' % threading.currentThread().getName() 13 event.wait() 14 15 # 收到事件后進入運行狀態 16 print '%s recv event.' % threading.currentThread().getName() 17 18 t1 = threading.Thread(target=func) 19 t2 = threading.Thread(target=func) 20 t1.start() 21 t2.start() 22 23 time.sleep(2) 24 25 # 發送事件通知 26 print 'MainThread set event.' 27 event.set()
參考文獻:
python多線程學習小結:http://www.myexception.cn/perl-python/1688021.html
python線程指南:http://www.cnblogs.com/huxi/archive/2010/06/26/1765808.html
threading.RLock和threading.Lock:http://blog.sina.com.cn/s/blog_5dd2af0901012rad.html
python的進程、線程與協程:http://www.cnblogs.com/wupeiqi/articles/5040827.html