本文介紹Python中的線程同步對象,主要涉及 thread 和 threading 模塊。python
threading 模塊提供的線程同步原語包括:Lock、RLock、Condition、Event、Semaphore等對象。函數
1. Lockui
1.1 Lock對象的建立spa
Lock是Python中最底層的同步機制,直接由底層模塊 thread 實現,每一個lock對象只有兩種狀態——上鎖和未上鎖,不一樣於下文的RLock對象,Lock對象是不可重入的,也沒有所屬的線程這個概念。線程
能夠經過下面兩種方式建立一個Lock對象,新建立的 Lock 對象處於未上鎖的狀態:對象
thread.allocate_lock() threading.Lock()
但他們本質上都是在 thread 模塊中實現的。blog
例如:事件
>>> l = threading.Lock() >>> type(l) <type 'thread.lock'> >>> l <thread.lock object at 0x0000000001C8F190>
1.2 lock對象的方法資源
lock對象提供三種方法:acquire()、locked()和release()get
l.acquire(wait=True)
該函數須要結合參數 wait 進行討論:
1. 當 wait 是 False 時,若是 l 沒有上鎖,那麼acquire()調用將l上鎖,而後返回True;
2. 當 wait 是 False 時,若是 l 已經上鎖,那麼acquire()調用對 l 沒有影響,而後返回False;
3. 當 wait 是 True 時,若是 l 沒有上鎖,acquire()調用將其上鎖,而後返回True;
4. 當 wait 是 True 時,若是 l 已經上鎖,此時調用 l.acquire() 的線程將會阻塞,直到其餘線程調用 l.release(),這裏須要注意的是,就算這個線程是最後一個鎖住 l 的線程,只要它以wait=True調用了acquire(),那它就會阻塞,由於Lock原語是不支持重入的。
可將,只要 l 沒有上鎖,調用 acquire()的結果是相同的。當l 上鎖了,而 wait=False 時,線程會當即獲得一個返回值,不會阻塞在等待鎖上面;而 wait = True時,線程會阻塞等待其餘的線程釋放該鎖,因此,一個鎖上面可能有多個處於阻塞等待狀態的線程。
l.locked()
判斷 l 當前是否上鎖,若是上鎖,返回True,不然返回False。
l.release()
解開 l 上的鎖,要求:
l一旦經過release()解開,以前等待它(調用過 l.acquire())的全部線程中,只有一個會被當即被喚醒,而後得到這個鎖。
2. RLock 可重入鎖
2.1 RLock對象的建立
RLock是可重入鎖,提供和lock對象相同的方法,可重入鎖的特色是
經過:
>>> rl = threading.RLock()
能夠建立一個可重入鎖。
2.2 rlock對象的方法
rlock()對象提供和Lock對象相同的acquire()和release()方法。
3. Condition 條件變量
3.1 Condition 對象的獲取
condition對象封裝了一個lock或rlock對象,經過實例化Condition類來得到一個condition對象:
c = threading.Condition(lock=None)
正如前面說的,condition對象的是基於Lock對象RLock對象的,若是建立 condition 對象時沒傳入 lock 對象,則會新建立一個RLock對象。
3.2 Condition 對象的方法
Condition對象封裝在一個Lock或RLock對象之上,提供的方法有:acquire(wait=1)、release()、notify()、notifyAll()和wait(timeout=None)
c.acquire(wait=1)
c.release()
本質上, condition對象的 acquire() 方法和 release() 方法都是底層鎖對象的對應方法,在調用condition對象的其餘方法以前,都應該確保線程已經拿到了condition對象對應的鎖,也就是調用過 acquire()。
c.notify() c.notify_all()
notify()喚醒一個等待 c 的線程,notify_all() 則會喚醒全部等待 c 的線程;
線程在調用 notify() 和 notifyAll() 以前必須已經得到 c 對應的鎖,不然拋出 RuntimeError。
notify() 和 notifyAll() 並不會致使線程釋放鎖,可是notify() 和 notify_all()以後,喚醒了其餘的等待線程,當前線程已經準備釋放鎖,所以線程一般會緊接着調用 release() 釋放鎖。
c.wait(timeout=None)
wait()最大的特色是調用wait()的線程必須已經acquire()了 c ,調用wait()將會使這個線程放棄 c,線程在此阻塞,而後當wait()返回時,這個線程每每又拿到了 c 。這個描述比較繞,看一個直觀一點的:
一個線程想要對臨界資源進行操做,首先要得到 c ,得到 c 後,它判斷臨界資源的狀態對不對,發現不對,就調用 wait()放掉手中的 c ,這時候實際上就是在等其餘的線程來更新臨界資源的狀態了。當某個其餘的線程修改了臨界資源的狀態,而後喚醒等待 c 的線程,這時咱們這個線程又拿到 c (假設很幸運地搶到了),就能夠繼續執行了。
若是臨界資源一直不對,而咱們這個線程又搶到了 c ,就能夠經過一個循環,不斷地釋放掉不須要的鎖,直到臨界資源的狀態符合咱們的要求。
例如:
# 消費者 cv.acquire() while not an_item_is_available(): cv.wait() get_an_available_item() cv.release() # 生產者 cv.acquire() make_an_item_available() cv.notify() cv.release()
這個例子中,消費者在產品沒有被生產出來以前,就算拿到 c ,也會當即調用 wait() 釋放,當產品被生產出來後,生產者喚醒一個消費者,消費者從新回到 wait() 阻塞的地方,發現產品已經就緒,因而消費產品,最後釋放 c 。
4 Event 事件
4.1 Event 對象的建立
Event對象可讓任何數量的線程暫停和等待,event 對象對應一個 True 或 False 的狀態(flag),剛建立的event對象的狀態爲False。經過實例化Event類來得到一個event對象:
e = threading.Event()
剛建立的event對象 e,它的狀態爲 False。
4.2 Event 對象的方法
e.clear()
將 e 的狀態設置爲 False。
e.set()
將 e 的狀態設置爲 True。
此時全部等待 e 的線程都被喚醒進入就緒狀態。
e.is_set()
返回 e 的 狀態——True 或 False。
e.wait(timeout=None)
若是 e 的狀態爲True,wait()當即返回True,不然線程會阻塞直到超時或者其餘的線程調用了e.set()。
5. Semaphore 信號量
5.1 Semaphore 對象的建立
信號量無疑是線程同步中最經常使用的技術了,信號量是一類通用的鎖,鎖的狀態一般就是真或假,可是信號量有一個初始值,這個值每每反映了固定的資源量。
經過調用:
s = threading.Semaphore(n=1)
建立一個Python信號量對象,參數 n 指定了信號量的初值。
5.2 Semaphore對象的方法
s.acquire(wait=True)
s.release()