在Python线程中,`threading`模块提供了条件变量`condition`来支持线程之间的复杂同步。条件变量运行一个或多个线程等待,直到另一个线程发出特定的通知信号。条件变量通常与锁(`Lock`或`RLock`)一起使用。
`threading.Condition()`的锁对象可以通过传入的方式获得,或者在缺省的情况下自动创建(此时自动创建的锁对象为递归锁对象`RLock`)。当多个条件变量需要共享同一个锁时,传入一个锁很有用。锁是条件对象的一部分,你不必单独跟踪它。
条件变量遵循上下文管理器协议,使用with语句会在它包围的代码块内获取关联的锁。
`Condition`对象的方法
acquire()
和release()
:获取和释放底层锁。wait(timeout=None)
:线程等待,直到接收到通知或者超时。线程在调用wait()
时会释放锁,并在被唤醒后重新获取锁。当提供了 timeout 参数且不是None
时,它应该是一个浮点数,代表操作的超时时间,以秒为单位(可以为小数)。返回True
,除非提供的 timeout 过期,这种情况下返回False
。- wait_for(predicate, tomeout=None):等待,直到条件计算为真。 predicate 应该是一个可调用对象而且它的返回值可被解释为一个布尔值。可以提供 timeout 参数给出最大等待时间。这个实用方法会重复地调用 wait() 直到满足判断式或者发生超时。返回值是判断式最后一个返回值,而且如果方法发生超时会返回
False
。 notify()
:通知一个等待的线程。notify_all()
:通知所有等待的线程。
注意: notify() 方法和 notify_all() 方法并不会释放锁,这意味着被唤醒的线程不会立即从它们的 wait() 方法调用中返回,而是会在调用了 notify() 方法或 notify_all() 方法的线程最终放弃了锁的所有权后返回。
使用场景:
线程间的协作:一个线程等待特定条件的达成,而另一个线程达成该条件并通知等待线程。
生产者-消费者模型:生产者线程生成资源并通知消费者线程使用资源。
下面展示一个如何使用condition对象实现生成者-消费者模型
import threading
import time
shared_resource = []
MAX_QUEUE_SIZE = 5
condition = threading.Condition()
def producer():
while True:
with condition:
if len(shared_resource) >= MAX_QUEUE_SIZE:
print("缓冲区已满,生产者等待")
condition.wait() # 等待直到队列有空间
shared_resource.append("产品")
print(f"生产了一个产品,当前缓冲区:{shared_resource}")
condition.notify() # 通知消费者有新产品
time.sleep(0.5)
def consumer():
while True:
with condition:
if not shared_resource:
print("缓冲区为空,消费者等待")
condition.wait() # 等待直到队列不为空
item = shared_resource.pop(0)
print(f"消费了一个产品:{item}, 当前缓冲区:{shared_resource}")
condition.notify() # 通知生产者可以生产
time.sleep(0.5)
producer_thread = threading.Thread(target=producer)
consumer_thread = threading.Thread(target=consumer)
producer_thread.start()
consumer_thread.start()
上面主要包括生产者线程和消费者线程。生产者生成一个随机数并将其添加到队列中。如果队列已满,生产者等待;否则,生产者通知消费者有新产品可用。消费者从队列中取出一个产品。如果队列为空,消费者等待;否则,消费者通知生产者可以继续生产。
使用
while
循环检查所要求的条件成立与否是有必要的,因为 wait() 方法可能要经过不确定长度的时间后才会返回,而此时导致 notify() 方法调用的那个条件可能已经不再成立。这是多线程编程所固有的问题。