多個執行線程經常要共用資料,如果僅僅讀取共用資料還好,但是如果多個線程要修改共用資料的話就可能出現無法預料的結果。
同步“鎖”機制
鎖對象用threading.RLock類建立
mylock = threading.RLock()
如何使用鎖來同步線程呢?線程可以使用鎖的acquire() (獲得)方法,這樣鎖就進入“locked”狀態。每次只有一個線程可以獲得鎖。如果當另一個線程試圖獲得這個鎖的時候,就會被系統變為“blocked”狀態,直到那個擁有鎖的線程調用鎖的release() (釋放)方法,這樣鎖就會進入“unlocked”狀態。
“blocked”狀態的線程就會收到一個通知,並有權利獲得鎖。如果多個線程處於“blocked”狀態,所有線程都會先解除“blocked”狀態,然後系統選擇一個線程來獲得鎖,其他的線程繼續沉默(“blocked”)。
import threading
mylock = threading.RLock()
class mythread(threading.Thread)
...
def run(self ...):
... #此處 不可以 放置修改共用資料的代碼
mylock.acquire()
... #此處 可以 放置修改共用資料的代碼
mylock.release()
... #此處 不可以 放置修改共用資料的代碼
我們把修改共用資料的代碼稱為“臨界區”,必須將所有“臨界區”都封閉在同一鎖對象的acquire()和release()方法調用之間。
使用條件變數
鎖只能提供最基本的同步層級。有時需要更複雜的線程同步,例如只在發生某些事件時才訪問一個臨界區(例如當某個數值改變時)。這就要使用“條件變數”。 條件變數用threading.Condition類建立
mycondition = threading.Condition()
條件變數是如何工作的呢?首先一個線程成功獲得一個條件變數後,調用此條件變數的wait()方法會導致這個線程釋放這個鎖,並進入“blocked”狀態,直到另一個線程調用同一個條件變數的notify()方法來喚醒那個進入“blocked”狀態的線程。如果調用這個條件變數的notifyAll()方法的話就會喚醒所有的在等待的線程。
如果程式或者線程永遠處於“blocked”狀態的話,就會發生死結。所以如果使用了鎖、條件變數等同步機制的話,一定要注意仔細檢查,防止死結情況的發生。對於可能產生異常的臨界區要使用異常處理機制中的finally子句來保證釋放鎖。等待一個條件變數的線程必須用notify()方法顯式的喚醒,否則就永遠沉默。保證每一個wait()方法調用都有一個相對應的notify()調用,當然也可以調用notifyAll()方法以防萬一。