Python線程指南 ---轉自 http://www.cnblogs.com/huxi/archive/2010/06/26/1765808.html
本文介紹了Python對于線程的支持,包括“學會”多線程編程需要掌握的基礎以及Python兩個線程標準庫的完整介紹及使用示例。
注意:本文基于Python2.4完成,;如果看到不明白的詞匯請記得百度谷歌或維基,whatever。
尊重作者的勞動,轉載請注明作者及原文地址 >.<
1. 線程基礎
1.1. 線程狀態
線程有5種狀態,狀態轉換的過程如下圖所示:
1.2. 線程同步(鎖)
多線程的優勢在于可以同時運行多個任務(至少感覺起來是這樣)。但是當線程需要共享數據時,可能存在數據不同步的問題。考慮這樣一種情況:一個列表里所有元素都是0,線程"set"從后向前把所有元素改成1,而線程"print"負責從前往后讀取列表并打印。那么,可能線程"set"開始改的時候,線程"print"便來打印列表了,輸出就成了一半0一半1,這就是數據的不同步。為了避免這種情況,引入了鎖的概念。
鎖有兩種狀態——鎖定和未鎖定。每當一個線程比如"set"要訪問共享數據時,必須先獲得鎖定;如果已經有別的線程比如"print"獲得鎖定了,那么就讓線程"set"暫停,也就是同步阻塞;等到線程"print"訪問完畢,釋放鎖以后,再讓線程"set"繼續。經過這樣的處理,打印列表時要么全部輸出0,要么全部輸出1,不會再出現一半0一半1的尷尬場面。
線程與鎖的交互如下圖所示:
1.3. 線程通信(條件變量)
然而還有另外一種尷尬的情況:列表并不是一開始就有的;而是通過線程"create"創建的。如果"set"或者"print" 在"create"還沒有運行的時候就訪問列表,將會出現一個異常。使用鎖可以解決這個問題,但是"set"和"print"將需要一個無限循環——他們不知道"create"什么時候會運行,讓"create"在運行后通知"set"和"print"顯然是一個更好的解決方案。于是,引入了條件變量。
條件變量允許線程比如"set"和"print"在條件不滿足的時候(列表為None時)等待,等到條件滿足的時候(列表已經創建)發出一個通知,告訴"set" 和"print"條件已經有了,你們該起床干活了;然后"set"和"print"才繼續運行。
線程與條件變量的交互如下圖所示:
??
1.4. 線程運行和阻塞的狀態轉換
最后看看線程運行和阻塞狀態的轉換。
阻塞有三種情況:?
同步阻塞是指處于競爭鎖定的狀態,線程請求鎖定時將進入這個狀態,一旦成功獲得鎖定又恢復到運行狀態;?
等待阻塞是指等待其他線程通知的狀態,線程獲得條件鎖定后,調用“等待”將進入這個狀態,一旦其他線程發出通知,線程將進入同步阻塞狀態,再次競爭條件鎖定;?
而其他阻塞是指調用time.sleep()、anotherthread.join()或等待IO時的阻塞,這個狀態下線程不會釋放已獲得的鎖定。
tips: 如果能理解這些內容,接下來的主題將是非常輕松的;并且,這些內容在大部分流行的編程語言里都是一樣的。(意思就是非看懂不可 >_< 嫌作者水平低找別人的教程也要看懂)
2. thread
Python通過兩個標準庫thread和threading提供對線程的支持。thread提供了低級別的、原始的線程以及一個簡單的鎖。
| 12 | ????# 這個方法與thread.exit_thread()等價 |
| 13 | ????thread.exit()?# 當func返回時,線程同樣會結束 |
| 16 | # 這個方法與thread.start_new_thread()等價 |
| 18 | thread.start_new(func, ())?# 方法沒有參數時需要傳入空tuple |
| 20 | # 創建一個鎖(LockType,不能直接實例化) |
| 21 | # 這個方法與thread.allocate_lock()等價 |
| 22 | lock?=?thread.allocate() |
| 31 | # 可選的timeout參數不填時將一直阻塞直到獲得鎖定 |
| 39 | # thread模塊提供的線程都將在主線程結束后同時結束 |
thread 模塊提供的其他方法:?
thread.interrupt_main(): 在其他線程中終止主線程。?
thread.get_ident(): 獲得一個代表當前線程的魔法數字,常用于從一個字典中獲得線程相關的數據。這個數字本身沒有任何含義,并且當線程結束后會被新線程復用。
thread還提供了一個ThreadLocal類用于管理線程相關的數據,名為 thread._local,threading中引用了這個類。
由于thread提供的線程功能不多,無法在主線程結束后繼續運行,不提供條件變量等等原因,一般不使用thread模塊,這里就不多介紹了。
3. threading
threading基于Java的線程模型設計。鎖(Lock)和條件變量(Condition)在Java中是對象的基本行為(每一個對象都自帶了鎖和條件變量),而在Python中則是獨立的對象。Python Thread提供了Java Thread的行為的子集;沒有優先級、線程組,線程也不能被停止、暫停、恢復、中斷。Java Thread中的部分被Python實現了的靜態方法在threading中以模塊方法的形式提供。
threading 模塊提供的常用方法:?
threading.currentThread(): 返回當前的線程變量。?
threading.enumerate(): 返回一個包含正在運行的線程的list。正在運行指線程啟動后、結束前,不包括啟動前和終止后的線程。?
threading.activeCount(): 返回正在運行的線程數量,與len(threading.enumerate())有相同的結果。
threading模塊提供的類:??
Thread, Lock, Rlock, Condition, [Bounded]Semaphore, Event, Timer, local.
3.1. Thread
Thread是線程類,與Java類似,有兩種使用方法,直接傳入要運行的方法或從Thread繼承并覆蓋run():
| 04 | # 方法1:將要執行的方法作為參數傳給Thread的構造方法 |
| 06 | ????print?'func() passed to Thread' |
| 08 | t?=?threading.Thread(target=func) |
| 11 | # 方法2:從Thread繼承,并重寫run() |
| 12 | class?MyThread(threading.Thread): |
| 14 | ????????print?'MyThread extended from Thread' |
構造方法:?
Thread(group=None, target=None, name=None, args=(), kwargs={})?
group: 線程組,目前還沒有實現,庫引用中提示必須是None;?
target: 要執行的方法;?
name: 線程名;?
args/kwargs: 要傳入方法的參數。
實例方法:?
isAlive(): 返回線程是否在運行。正在運行指啟動后、終止前。?
get/setName(name): 獲取/設置線程名。?
is/setDaemon(bool): 獲取/設置是否守護線程。初始值從創建該線程的線程繼承。當沒有非守護線程仍在運行時,程序將終止。?
start(): 啟動線程。?
join([timeout]): 阻塞當前上下文環境的線程,直到調用此方法的線程終止或到達指定的timeout(可選參數)。
一個使用join()的例子:
| 06 | ????print?'in threadContext.' |
| 09 | ????# 將阻塞tContext直到threadJoin終止。 |
| 13 | ????print?'out threadContext.' |
| 16 | ????print?'in threadJoin.' |
| 18 | ????print?'out threadJoin.' |
| 20 | tJoin?=?threading.Thread(target=join) |
| 21 | tContext?=?threading.Thread(target=context, args=(tJoin,)) |
運行結果:
in threadContext.?
in threadJoin.?
out threadJoin.?
out threadContext.
3.2. Lock
Lock(指令鎖)是可用的最低級的同步指令。Lock處于鎖定狀態時,不被特定的線程擁有。Lock包含兩種狀態——鎖定和非鎖定,以及兩個基本的方法。
可以認為Lock有一個鎖定池,當線程請求鎖定時,將線程至于池中,直到獲得鎖定后出池。池中的線程處于狀態圖中的同步阻塞狀態。
構造方法:?
Lock()
實例方法:?
acquire([timeout]): 使線程進入同步阻塞狀態,嘗試獲得鎖定。?
release(): 釋放鎖。使用前線程必須已獲得鎖定,否則將拋出異常。
| 06 | lock?=?threading.Lock() |
| 10 | ????print?'%s acquire lock...'?%?threading.currentThread().getName() |
| 12 | ????# 調用acquire([timeout])時,線程將一直阻塞, |
| 13 | ????# 直到獲得鎖定或者直到timeout秒后(timeout參數可選)。 |
| 16 | ????????print?'%s get the lock.'?%?threading.currentThread().getName() |
| 19 | ????????print?'%s release lock...'?%?threading.currentThread().getName() |
| 21 | ????????# 調用release()將釋放鎖。 |
| 24 | t1?=?threading.Thread(target=func) |
| 25 | t2?=?threading.Thread(target=func) |
| 26 | t3?=?threading.Thread(target=func) |
3.3. RLock
RLock(可重入鎖)是一個可以被同一個線程請求多次的同步指令。RLock使用了“擁有的線程”和“遞歸等級”的概念,處于鎖定狀態時,RLock被某個線程擁有。擁有RLock的線程可以再次調用acquire(),釋放鎖時需要調用release()相同次數。
可以認為RLock包含一個鎖定池和一個初始值為0的計數器,每次成功調用 acquire()/release(),計數器將+1/-1,為0時鎖處于未鎖定狀態。
構造方法:?
RLock()
實例方法:?
acquire([timeout])/release(): 跟Lock差不多。
| 05 | rlock?=?threading.RLock() |
| 09 | ????print?'%s acquire lock...'?%?threading.currentThread().getName() |
| 10 | ????if?rlock.acquire(): |
| 11 | ????????print?'%s get the lock.'?%?threading.currentThread().getName() |
| 15 | ????????print?'%s acquire lock again...'?%?threading.currentThread().getName() |
| 16 | ????????if?rlock.acquire(): |
| 17 | ????????????print?'%s get the lock.'?%?threading.currentThread().getName() |
| 18 | ????????????time.sleep(2) |
| 21 | ????????print?'%s release lock...'?%?threading.currentThread().getName() |
| 22 | ????????rlock.release() |
| 26 | ????????print?'%s release lock...'?%?threading.currentThread().getName() |
| 27 | ????????rlock.release() |
| 29 | t1?=?threading.Thread(target=func) |
| 30 | t2?=?threading.Thread(target=func) |
| 31 | t3?=?threading.Thread(target=func) |
3.4. Condition
Condition(條件變量)通常與一個鎖關聯。需要在多個Contidion中共享一個鎖時,可以傳遞一個Lock/RLock實例給構造方法,否則它將自己生成一個RLock實例。
可以認為,除了Lock帶有的鎖定池外,Condition還包含一個等待池,池中的線程處于狀態圖中的等待阻塞狀態,直到另一個線程調用notify()/notifyAll()通知;得到通知后線程進入鎖定池等待鎖定。
構造方法:?
Condition([lock/rlock])
實例方法:?
acquire([timeout])/release(): 調用關聯的鎖的相應方法。?
wait([timeout]): 調用這個方法將使線程進入Condition的等待池等待通知,并釋放鎖。使用前線程必須已獲得鎖定,否則將拋出異常。?
notify(): 調用這個方法將從等待池挑選一個線程并通知,收到通知的線程將自動調用acquire()嘗試獲得鎖定(進入鎖定池);其他線程仍然在等待池中。調用這個方法不會釋放鎖定。使用前線程必須已獲得鎖定,否則將拋出異常。?
notifyAll(): 調用這個方法將通知等待池中所有的線程,這些線程都將進入鎖定池嘗試獲得鎖定。調用這個方法不會釋放鎖定。使用前線程必須已獲得鎖定,否則將拋出異常。
例子是很常見的生產者/消費者模式:
| 08 | con?=?threading.Condition() |
| 16 | ????????????if?product?is?None: |
| 17 | ????????????????print?'produce...' |
| 18 | ????????????????product?=?'anything' |
| 20 | ????????????????# 通知消費者,商品已經生產 |
| 21 | ????????????????con.notify() |
| 25 | ????????????time.sleep(2) |
| 33 | ????????????if?product?is?not?None: |
| 34 | ????????????????print?'consume...' |
| 35 | ????????????????product?=?None |
| 37 | ????????????????# 通知生產者,商品已經沒了 |
| 38 | ????????????????con.notify() |
| 42 | ????????????time.sleep(2) |
| 44 | t1?=?threading.Thread(target=produce) |
| 45 | t2?=?threading.Thread(target=consume) |
3.5. Semaphore/BoundedSemaphore
Semaphore(信號量)是計算機科學史上最古老的同步指令之一。Semaphore管理一個內置的計數器,每當調用acquire()時-1,調用release() 時+1。計數器不能小于0;當計數器為0時,acquire()將阻塞線程至同步鎖定狀態,直到其他線程調用release()。
基于這個特點,Semaphore經常用來同步一些有“訪客上限”的對象,比如連接池。
BoundedSemaphore 與Semaphore的唯一區別在于前者將在調用release()時檢查計數器的值是否超過了計數器的初始值,如果超過了將拋出一個異常。
構造方法:?
Semaphore(value=1): value是計數器的初始值。
實例方法:?
acquire([timeout]): 請求Semaphore。如果計數器為0,將阻塞線程至同步阻塞狀態;否則將計數器-1并立即返回。?
release(): 釋放Semaphore,將計數器+1,如果使用BoundedSemaphore,還將進行釋放次數檢查。release()方法不檢查線程是否已獲得 Semaphore。
| 06 | semaphore?=?threading.Semaphore(2) |
| 10 | ????# 請求Semaphore,成功后計數器-1;計數器為0時阻塞 |
| 11 | ????print?'%s acquire semaphore...'?%?threading.currentThread().getName() |
| 12 | ????if?semaphore.acquire(): |
| 14 | ????????print?'%s get semaphore'?%?threading.currentThread().getName() |
| 17 | ????????# 釋放Semaphore,計數器+1 |
| 18 | ????????print?'%s release semaphore'?%?threading.currentThread().getName() |
| 19 | ????????semaphore.release() |
| 21 | t1?=?threading.Thread(target=func) |
| 22 | t2?=?threading.Thread(target=func) |
| 23 | t3?=?threading.Thread(target=func) |
| 24 | t4?=?threading.Thread(target=func) |
| 32 | # 沒有獲得semaphore的主線程也可以調用release |
| 33 | # 若使用BoundedSemaphore,t4釋放semaphore時將拋出異常 |
| 34 | print?'MainThread release semaphore without acquire' |
3.6. Event
Event(事件)是最簡單的線程通信機制之一:一個線程通知事件,其他線程等待事件。Event內置了一個初始為False的標志,當調用set()時設為True,調用clear()時重置為 False。wait()將阻塞線程至等待阻塞狀態。
Event其實就是一個簡化版的 Condition。Event沒有鎖,無法使線程進入同步阻塞狀態。
構造方法:?
Event()
實例方法:?
isSet(): 當內置標志為True時返回True。?
set(): 將標志設為True,并通知所有處于等待阻塞狀態的線程恢復運行狀態。?
clear(): 將標志設為False。?
wait([timeout]): 如果標志為True將立即返回,否則阻塞線程至等待阻塞狀態,等待其他線程調用set()。
| 05 | event?=?threading.Event() |
| 09 | ????print?'%s wait for event...'?%?threading.currentThread().getName() |
| 13 | ????print?'%s recv event.'?%?threading.currentThread().getName() |
| 15 | t1?=?threading.Thread(target=func) |
| 16 | t2?=?threading.Thread(target=func) |
| 23 | print?'MainThread set event.' |
3.7. Timer
Timer(定時器)是Thread的派生類,用于在指定時間后調用一個方法。
構造方法:?
Timer(interval, function, args=[], kwargs={})?
interval: 指定的時間?
function: 要執行的方法?
args/kwargs: 方法的參數
實例方法:?
Timer從Thread派生,沒有增加實例方法。
| 5 | ????print?'hello timer!' |
| 7 | timer?=?threading.Timer(5, func) |
3.8. local
local是一個小寫字母開頭的類,用于管理 thread-local(線程局部的)數據。對于同一個local,線程無法訪問其他線程設置的屬性;線程設置的屬性不會被其他線程設置的同名屬性替換。
可以把local看成是一個“線程-屬性字典”的字典,local封裝了從自身使用線程作為 key檢索對應的屬性字典、再使用屬性名作為key檢索屬性值的細節。
| 04 | local?=?threading.local() |
| 08 | ????local.tname?=?'notmain' |
| 11 | t1?=?threading.Thread(target=func) |
?
熟練掌握Thread、Lock、Condition就可以應對絕大多數需要使用線程的場合,某些情況下local也是非常有用的東西。本文的最后使用這幾個類展示線程基礎中提到的場景:
| 05 | condition?=?threading.Condition() |
| 08 | ????if?condition.acquire(): |
| 09 | ????????while?alist?is?None: |
| 10 | ????????????condition.wait() |
| 11 | ????????for?i?in?range(len(alist))[::-1]: |
| 12 | ????????????alist[i]?=?1 |
| 13 | ????????condition.release() |
| 16 | ????if?condition.acquire(): |
| 17 | ????????while?alist?is?None: |
| 18 | ????????????condition.wait() |
| 19 | ????????for?i?in?alist: |
| 22 | ????????condition.release() |
| 26 | ????if?condition.acquire(): |
| 27 | ????????if?alist?is?None: |
| 28 | ????????????alist?=?[0?for?i?in?range(10)] |
| 29 | ????????????condition.notifyAll() |
| 30 | ????????condition.release() |
| 32 | tset?=?threading.Thread(target=doSet,name='tset') |
| 33 | tprint?=?threading.Thread(target=doPrint,name='tprint') |
| 34 | tcreate?=?threading.Thread(target=doCreate,name='tcreate') |
| 37 | tcreate.start() 本文介紹了Python對于線程的支持,包括“學會”多線程編程需要掌握的基礎以及Python兩個線程標準庫的完整介紹及使用示例。 注意:本文基于Python2.4完成,;如果看到不明白的詞匯請記得百度谷歌或維基,whatever。 尊重作者的勞動,轉載請注明作者及原文地址 >.< 1. 線程基礎 1.1. 線程狀態 線程有5種狀態,狀態轉換的過程如下圖所示: 1.2. 線程同步(鎖) 多線程的優勢在于可以同時運行多個任務(至少感覺起來是這樣)。但是當線程需要共享數據時,可能存在數據不同步的問題。考慮這樣一種情況:一個列表里所有元素都是0,線程"set"從后向前把所有元素改成1,而線程"print"負責從前往后讀取列表并打印。那么,可能線程"set"開始改的時候,線程"print"便來打印列表了,輸出就成了一半0一半1,這就是數據的不同步。為了避免這種情況,引入了鎖的概念。 鎖有兩種狀態——鎖定和未鎖定。每當一個線程比如"set"要訪問共享數據時,必須先獲得鎖定;如果已經有別的線程比如"print"獲得鎖定了,那么就讓線程"set"暫停,也就是同步阻塞;等到線程"print"訪問完畢,釋放鎖以后,再讓線程"set"繼續。經過這樣的處理,打印列表時要么全部輸出0,要么全部輸出1,不會再出現一半0一半1的尷尬場面。 線程與鎖的交互如下圖所示: 1.3. 線程通信(條件變量) 然而還有另外一種尷尬的情況:列表并不是一開始就有的;而是通過線程"create"創建的。如果"set"或者"print" 在"create"還沒有運行的時候就訪問列表,將會出現一個異常。使用鎖可以解決這個問題,但是"set"和"print"將需要一個無限循環——他們不知道"create"什么時候會運行,讓"create"在運行后通知"set"和"print"顯然是一個更好的解決方案。于是,引入了條件變量。 條件變量允許線程比如"set"和"print"在條件不滿足的時候(列表為None時)等待,等到條件滿足的時候(列表已經創建)發出一個通知,告訴"set" 和"print"條件已經有了,你們該起床干活了;然后"set"和"print"才繼續運行。 線程與條件變量的交互如下圖所示: ?? 1.4. 線程運行和阻塞的狀態轉換 最后看看線程運行和阻塞狀態的轉換。 阻塞有三種情況:? 同步阻塞是指處于競爭鎖定的狀態,線程請求鎖定時將進入這個狀態,一旦成功獲得鎖定又恢復到運行狀態;? 等待阻塞是指等待其他線程通知的狀態,線程獲得條件鎖定后,調用“等待”將進入這個狀態,一旦其他線程發出通知,線程將進入同步阻塞狀態,再次競爭條件鎖定;? 而其他阻塞是指調用time.sleep()、anotherthread.join()或等待IO時的阻塞,這個狀態下線程不會釋放已獲得的鎖定。 tips: 如果能理解這些內容,接下來的主題將是非常輕松的;并且,這些內容在大部分流行的編程語言里都是一樣的。(意思就是非看懂不可 >_< 嫌作者水平低找別人的教程也要看懂) 2. thread Python通過兩個標準庫thread和threading提供對線程的支持。thread提供了低級別的、原始的線程以及一個簡單的鎖。 | 12 | ????# 這個方法與thread.exit_thread()等價 | | 13 | ????thread.exit()?# 當func返回時,線程同樣會結束 | | 16 | # 這個方法與thread.start_new_thread()等價 | | 18 | thread.start_new(func, ())?# 方法沒有參數時需要傳入空tuple | | 20 | # 創建一個鎖(LockType,不能直接實例化) | | 21 | # 這個方法與thread.allocate_lock()等價 | | 22 | lock?=?thread.allocate() | | 31 | # 可選的timeout參數不填時將一直阻塞直到獲得鎖定 | | 39 | # thread模塊提供的線程都將在主線程結束后同時結束 | thread 模塊提供的其他方法:? thread.interrupt_main(): 在其他線程中終止主線程。? thread.get_ident(): 獲得一個代表當前線程的魔法數字,常用于從一個字典中獲得線程相關的數據。這個數字本身沒有任何含義,并且當線程結束后會被新線程復用。 thread還提供了一個ThreadLocal類用于管理線程相關的數據,名為 thread._local,threading中引用了這個類。 由于thread提供的線程功能不多,無法在主線程結束后繼續運行,不提供條件變量等等原因,一般不使用thread模塊,這里就不多介紹了。 3. threading threading基于Java的線程模型設計。鎖(Lock)和條件變量(Condition)在Java中是對象的基本行為(每一個對象都自帶了鎖和條件變量),而在Python中則是獨立的對象。Python Thread提供了Java Thread的行為的子集;沒有優先級、線程組,線程也不能被停止、暫停、恢復、中斷。Java Thread中的部分被Python實現了的靜態方法在threading中以模塊方法的形式提供。 threading 模塊提供的常用方法:? threading.currentThread(): 返回當前的線程變量。? threading.enumerate(): 返回一個包含正在運行的線程的list。正在運行指線程啟動后、結束前,不包括啟動前和終止后的線程。? threading.activeCount(): 返回正在運行的線程數量,與len(threading.enumerate())有相同的結果。 threading模塊提供的類:?? Thread, Lock, Rlock, Condition, [Bounded]Semaphore, Event, Timer, local. 3.1. Thread Thread是線程類,與Java類似,有兩種使用方法,直接傳入要運行的方法或從Thread繼承并覆蓋run(): | 04 | # 方法1:將要執行的方法作為參數傳給Thread的構造方法 | | 06 | ????print?'func() passed to Thread' | | 08 | t?=?threading.Thread(target=func) | | 11 | # 方法2:從Thread繼承,并重寫run() | | 12 | class?MyThread(threading.Thread): | | 14 | ????????print?'MyThread extended from Thread' | 構造方法:? Thread(group=None, target=None, name=None, args=(), kwargs={})? group: 線程組,目前還沒有實現,庫引用中提示必須是None;? target: 要執行的方法;? name: 線程名;? args/kwargs: 要傳入方法的參數。 實例方法:? isAlive(): 返回線程是否在運行。正在運行指啟動后、終止前。? get/setName(name): 獲取/設置線程名。? is/setDaemon(bool): 獲取/設置是否守護線程。初始值從創建該線程的線程繼承。當沒有非守護線程仍在運行時,程序將終止。? start(): 啟動線程。? join([timeout]): 阻塞當前上下文環境的線程,直到調用此方法的線程終止或到達指定的timeout(可選參數)。 一個使用join()的例子: | 06 | ????print?'in threadContext.' | | 09 | ????# 將阻塞tContext直到threadJoin終止。 | | 13 | ????print?'out threadContext.' | | 16 | ????print?'in threadJoin.' | | 18 | ????print?'out threadJoin.' | | 20 | tJoin?=?threading.Thread(target=join) | | 21 | tContext?=?threading.Thread(target=context, args=(tJoin,)) | 運行結果: in threadContext.? in threadJoin.? out threadJoin.? out threadContext. 3.2. Lock Lock(指令鎖)是可用的最低級的同步指令。Lock處于鎖定狀態時,不被特定的線程擁有。Lock包含兩種狀態——鎖定和非鎖定,以及兩個基本的方法。 可以認為Lock有一個鎖定池,當線程請求鎖定時,將線程至于池中,直到獲得鎖定后出池。池中的線程處于狀態圖中的同步阻塞狀態。 構造方法:? Lock() 實例方法:? acquire([timeout]): 使線程進入同步阻塞狀態,嘗試獲得鎖定。? release(): 釋放鎖。使用前線程必須已獲得鎖定,否則將拋出異常。 | 06 | lock?=?threading.Lock() | | 10 | ????print?'%s acquire lock...'?%?threading.currentThread().getName() | | 12 | ????# 調用acquire([timeout])時,線程將一直阻塞, | | 13 | ????# 直到獲得鎖定或者直到timeout秒后(timeout參數可選)。 | | 16 | ????????print?'%s get the lock.'?%?threading.currentThread().getName() | | 19 | ????????print?'%s release lock...'?%?threading.currentThread().getName() | | 21 | ????????# 調用release()將釋放鎖。 | | 24 | t1?=?threading.Thread(target=func) | | 25 | t2?=?threading.Thread(target=func) | | 26 | t3?=?threading.Thread(target=func) | 3.3. RLock RLock(可重入鎖)是一個可以被同一個線程請求多次的同步指令。RLock使用了“擁有的線程”和“遞歸等級”的概念,處于鎖定狀態時,RLock被某個線程擁有。擁有RLock的線程可以再次調用acquire(),釋放鎖時需要調用release()相同次數。 可以認為RLock包含一個鎖定池和一個初始值為0的計數器,每次成功調用 acquire()/release(),計數器將+1/-1,為0時鎖處于未鎖定狀態。 構造方法:? RLock() 實例方法:? acquire([timeout])/release(): 跟Lock差不多。 | 05 | rlock?=?threading.RLock() | | 09 | ????print?'%s acquire lock...'?%?threading.currentThread().getName() | | 10 | ????if?rlock.acquire(): | | 11 | ????????print?'%s get the lock.'?%?threading.currentThread().getName() | | 15 | ????????print?'%s acquire lock again...'?%?threading.currentThread().getName() | | 16 | ????????if?rlock.acquire(): | | 17 | ????????????print?'%s get the lock.'?%?threading.currentThread().getName() | | 18 | ????????????time.sleep(2) | | 21 | ????????print?'%s release lock...'?%?threading.currentThread().getName() | | 22 | ????????rlock.release() | | 26 | ????????print?'%s release lock...'?%?threading.currentThread().getName() | | 27 | ????????rlock.release() | | 29 | t1?=?threading.Thread(target=func) | | 30 | t2?=?threading.Thread(target=func) | | 31 | t3?=?threading.Thread(target=func) | 3.4. Condition Condition(條件變量)通常與一個鎖關聯。需要在多個Contidion中共享一個鎖時,可以傳遞一個Lock/RLock實例給構造方法,否則它將自己生成一個RLock實例。 可以認為,除了Lock帶有的鎖定池外,Condition還包含一個等待池,池中的線程處于狀態圖中的等待阻塞狀態,直到另一個線程調用notify()/notifyAll()通知;得到通知后線程進入鎖定池等待鎖定。 構造方法:? Condition([lock/rlock]) 實例方法:? acquire([timeout])/release(): 調用關聯的鎖的相應方法。? wait([timeout]): 調用這個方法將使線程進入Condition的等待池等待通知,并釋放鎖。使用前線程必須已獲得鎖定,否則將拋出異常。? notify(): 調用這個方法將從等待池挑選一個線程并通知,收到通知的線程將自動調用acquire()嘗試獲得鎖定(進入鎖定池);其他線程仍然在等待池中。調用這個方法不會釋放鎖定。使用前線程必須已獲得鎖定,否則將拋出異常。? notifyAll(): 調用這個方法將通知等待池中所有的線程,這些線程都將進入鎖定池嘗試獲得鎖定。調用這個方法不會釋放鎖定。使用前線程必須已獲得鎖定,否則將拋出異常。 例子是很常見的生產者/消費者模式: | 08 | con?=?threading.Condition() | | 16 | ????????????if?product?is?None: | | 17 | ????????????????print?'produce...' | | 18 | ????????????????product?=?'anything' | | 20 | ????????????????# 通知消費者,商品已經生產 | | 21 | ????????????????con.notify() | | 25 | ????????????time.sleep(2) | | 33 | ????????????if?product?is?not?None: | | 34 | ????????????????print?'consume...' | | 35 | ????????????????product?=?None | | 37 | ????????????????# 通知生產者,商品已經沒了 | | 38 | ????????????????con.notify() | | 42 | ????????????time.sleep(2) | | 44 | t1?=?threading.Thread(target=produce) | | 45 | t2?=?threading.Thread(target=consume) | 3.5. Semaphore/BoundedSemaphore Semaphore(信號量)是計算機科學史上最古老的同步指令之一。Semaphore管理一個內置的計數器,每當調用acquire()時-1,調用release() 時+1。計數器不能小于0;當計數器為0時,acquire()將阻塞線程至同步鎖定狀態,直到其他線程調用release()。 基于這個特點,Semaphore經常用來同步一些有“訪客上限”的對象,比如連接池。 BoundedSemaphore 與Semaphore的唯一區別在于前者將在調用release()時檢查計數器的值是否超過了計數器的初始值,如果超過了將拋出一個異常。 構造方法:? Semaphore(value=1): value是計數器的初始值。 實例方法:? acquire([timeout]): 請求Semaphore。如果計數器為0,將阻塞線程至同步阻塞狀態;否則將計數器-1并立即返回。? release(): 釋放Semaphore,將計數器+1,如果使用BoundedSemaphore,還將進行釋放次數檢查。release()方法不檢查線程是否已獲得 Semaphore。 | 06 | semaphore?=?threading.Semaphore(2) | | 10 | ????# 請求Semaphore,成功后計數器-1;計數器為0時阻塞 | | 11 | ????print?'%s acquire semaphore...'?%?threading.currentThread().getName() | | 12 | ????if?semaphore.acquire(): | | 14 | ????????print?'%s get semaphore'?%?threading.currentThread().getName() | | 17 | ????????# 釋放Semaphore,計數器+1 | | 18 | ????????print?'%s release semaphore'?%?threading.currentThread().getName() | | 19 | ????????semaphore.release() | | 21 | t1?=?threading.Thread(target=func) | | 22 | t2?=?threading.Thread(target=func) | | 23 | t3?=?threading.Thread(target=func) | | 24 | t4?=?threading.Thread(target=func) | | 32 | # 沒有獲得semaphore的主線程也可以調用release | | 33 | # 若使用BoundedSemaphore,t4釋放semaphore時將拋出異常 | | 34 | print?'MainThread release semaphore without acquire' | 3.6. Event Event(事件)是最簡單的線程通信機制之一:一個線程通知事件,其他線程等待事件。Event內置了一個初始為False的標志,當調用set()時設為True,調用clear()時重置為 False。wait()將阻塞線程至等待阻塞狀態。 Event其實就是一個簡化版的 Condition。Event沒有鎖,無法使線程進入同步阻塞狀態。 構造方法:? Event() 實例方法:? isSet(): 當內置標志為True時返回True。? set(): 將標志設為True,并通知所有處于等待阻塞狀態的線程恢復運行狀態。? clear(): 將標志設為False。? wait([timeout]): 如果標志為True將立即返回,否則阻塞線程至等待阻塞狀態,等待其他線程調用set()。 | 05 | event?=?threading.Event() | | 09 | ????print?'%s wait for event...'?%?threading.currentThread().getName() | | 13 | ????print?'%s recv event.'?%?threading.currentThread().getName() | | 15 | t1?=?threading.Thread(target=func) | | 16 | t2?=?threading.Thread(target=func) | | 23 | print?'MainThread set event.' | 3.7. Timer Timer(定時器)是Thread的派生類,用于在指定時間后調用一個方法。 構造方法:? Timer(interval, function, args=[], kwargs={})? interval: 指定的時間? function: 要執行的方法? args/kwargs: 方法的參數 實例方法:? Timer從Thread派生,沒有增加實例方法。 | 5 | ????print?'hello timer!' | | 7 | timer?=?threading.Timer(5, func) | 3.8. local local是一個小寫字母開頭的類,用于管理 thread-local(線程局部的)數據。對于同一個local,線程無法訪問其他線程設置的屬性;線程設置的屬性不會被其他線程設置的同名屬性替換。 可以把local看成是一個“線程-屬性字典”的字典,local封裝了從自身使用線程作為 key檢索對應的屬性字典、再使用屬性名作為key檢索屬性值的細節。 | 04 | local?=?threading.local() | | 08 | ????local.tname?=?'notmain' | | 11 | t1?=?threading.Thread(target=func) | ? 熟練掌握Thread、Lock、Condition就可以應對絕大多數需要使用線程的場合,某些情況下local也是非常有用的東西。本文的最后使用這幾個類展示線程基礎中提到的場景: | 05 | condition?=?threading.Condition() | | 08 | ????if?condition.acquire(): | | 09 | ????????while?alist?is?None: | | 10 | ????????????condition.wait() | | 11 | ????????for?i?in?range(len(alist))[::-1]: | | 12 | ????????????alist[i]?=?1 | | 13 | ????????condition.release() | | 16 | ????if?condition.acquire(): | | 17 | ????????while?alist?is?None: | | 18 | ????????????condition.wait() | | 19 | ????????for?i?in?alist: | | 22 | ????????condition.release() | | 26 | ????if?condition.acquire(): | | 27 | ????????if?alist?is?None: | | 28 | ????????????alist?=?[0?for?i?in?range(10)] | | 29 | ????????????condition.notifyAll() | | 30 | ????????condition.release() | | 32 | tset?=?threading.Thread(target=doSet,name='tset') | | 33 | tprint?=?threading.Thread(target=doPrint,name='tprint') | | 34 | tcreate?=?threading.Thread(target=doCreate,name='tcreate') | |
posted on
2011-10-27 17:15 linyawen 閱讀(
...) 評論() 編輯 收藏
轉載于:https://www.cnblogs.com/linyawen/archive/2011/10/27/2226839.html
總結
以上是生活随笔為你收集整理的Python线程指南 ---转自 http://www.cnblogs.com/huxi/archive/2010/06/26/1765808.html的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。