Condition
class threading.Condition(lock=None
這個類實現條件變量對象。條件變量允許一個或多個線程等待,知道它們被另一個線程喚醒。
如果給出了lock參數而不是None,則它必須是Lcok或RLock對象,並以它作為底層的鎖。否則將默認創建一個RLock對象。
Condition遵循上下文管理協議。
方法:
acquire(*args)
獲取鎖。這個方法調用底層鎖的相應方法。
release()
釋放鎖。這個方法調用底層鎖的相應方法。
wait(timeout=None)
線程掛起,等待被喚醒(其他線程的notify方法)或者發生超時。調用該方法的線程必須先獲得鎖,否則引發RuntimeError。
該方法會釋放底層鎖,然后阻塞,直到它被另一個線程中的相同條件變量的notify()或notify_all()方法喚醒,或者發生超時。一旦被喚醒或超時,它會重新獲取鎖並返回。
返回值為True,如果給定timeout並發生超時,則返回False。
wait_for(predicate, timeout=None)
等待知道條件變量的返回值為True。predicate應該是一個返回值可以解釋為布爾值的可調用對象。可以設置timeout以給定最大等待時間。
該方法可以重復調用wait(),直到predicate的返回值解釋為True,或發生超時。該方法的返回值就是predicate的最后一個返回值,如果發生超時,返回值為False。
如果忽略超時功能,該方法大致相當於:
while not predicate():
con.wait()
它與wait()的規則相同:調用前必須先獲取鎖,阻塞時釋放鎖,並在被喚醒時重新獲取鎖並返回。
notify(n=1)
默認情況下,喚醒等待此條件變量的一個線程(如果有)。調用該方法的線程必須先獲得鎖,否則引發RuntimeError。
該方法最多喚醒n個等待中的線程,如果沒有線程在等待,它就是要給無動作的操作。
注意:要被喚醒的線程實際上不會馬上從wait()方法返回(喚醒),而是等到它重新獲取鎖。這是因為notify()並不會釋放鎖,需要線程本身來釋放(通過wait()或者release())
notify_all()
此方法類似於notify(),但喚醒的時所有等待的線程。
生產者與消費者 -- Condition版
場景:生產者一次性生產5個商品(生產過程中不可購買),之后通知消費者搶購,當商品賣完后,由消費者通知生產者開始生產。
# -*- coding:utf-8 -*-
import threading
import time
num = 0
con = threading.Condition()
class Producer(threading.Thread):
"""生產者"""
def run(self):
global num
# 獲取鎖
con.acquire()
while True:
num += 1
print('生產了1個,現在有{0}個'.format(num))
time.sleep(1)
if num >= 5:
print('已達到5個,不再生產')
# 喚醒消費者
con.notify()
# 等待-釋放鎖;被喚醒-獲取鎖
con.wait()
# 釋放鎖
con.release()
class Customer(threading.Thread):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.money = 7
def run(self):
global num
while self.money > 0:
# 由於場景是多個消費者進行搶購,如果將獲取鎖操作放在循環外(如生產者),
# 那么一個消費者線程被喚醒時會鎖住整個循環,無法實現另一個消費者的搶購。
# 在循環中添加一套"獲取鎖-釋放鎖",一個消費者購買完成后釋放鎖,其他消費者
# 就可以獲取鎖來參與購買。
con.acquire()
if num <= 0:
print('沒貨了,{0}通知生產者'.format(
threading.current_thread().name))
con.notify()
con.wait()
self.money -= 1
num -= 1
print('{0}消費了1個, 剩余{1}個'.format(
threading.current_thread().name, num))
con.release()
time.sleep(1)
print('{0}沒錢了-回老家'.format(threading.current_thread().name))
if __name__ == '__main__':
p = Producer(daemon=True)
c1 = Customer(name='Customer-1')
c2 = Customer(name='Customer-2')
p.start()
c1.start()
c2.start()
c1.join()
c2.join()
運行結果:
生產了1個,現在有1個
生產了1個,現在有2個
生產了1個,現在有3個
生產了1個,現在有4個
生產了1個,現在有5個
已達到5個,不再生產
Customer-1消費了1個, 剩余4個
Customer-2消費了1個, 剩余3個
Customer-1消費了1個, 剩余2個
Customer-2消費了1個, 剩余1個
Customer-1消費了1個, 剩余0個
沒貨了,Customer-2通知生產者
生產了1個,現在有1個
生產了1個,現在有2個
生產了1個,現在有3個
生產了1個,現在有4個
生產了1個,現在有5個
已達到5個,不再生產
Customer-1消費了1個, 剩余4個
Customer-2消費了1個, 剩余3個
Customer-1消費了1個, 剩余2個
Customer-2消費了1個, 剩余1個
Customer-1消費了1個, 剩余0個
沒貨了,Customer-2通知生產者
生產了1個,現在有1個
生產了1個,現在有2個
生產了1個,現在有3個
生產了1個,現在有4個
生產了1個,現在有5個
已達到5個,不再生產
Customer-1消費了1個, 剩余4個
Customer-2消費了1個, 剩余3個
Customer-2消費了1個, 剩余2個
Customer-1消費了1個, 剩余1個
Customer-1沒錢了-回老家
Customer-2消費了1個, 剩余0個
沒貨了,Customer-2通知生產者
生產了1個,現在有1個
生產了1個,現在有2個
生產了1個,現在有3個
生產了1個,現在有4個
生產了1個,現在有5個
已達到5個,不再生產
Customer-2消費了1個, 剩余4個
Customer-2沒錢了-回老家
