ConditionVariable 是 Ruby 提供的一个同步原语,用于在线程间进行协调和等待。它允许一个线程等待某个特定条件的发生,同时允许其他线程通知该条件已满足。这使得 ConditionVariable 成为了多线程程序设计中的一个重要工具。
ConditionVariable 的基本概念
ConditionVariable 是一种同步机制,它允许多个线程在某些条件满足时进行通信。这些条件通常由一个布尔表达式来表示,例如某个变量的值达到某个阈值或者某个资源变得可用等。ConditionVariable 通过 wait 和 signal 方法实现了线程间的协调:
wait: 使当前线程进入休眠状态,直到另一个线程调用signal或broadcast方法唤醒它。signal: 唤醒等待队列中的第一个线程。broadcast: 唤醒所有正在等待的线程。
创建 ConditionVariable 对象
创建 ConditionVariable 对象非常简单,只需要使用 ConditionVariable.new 方法即可:
cv = ConditionVariable.new
使用 ConditionVariable 的基本模式
等待条件满足
当你需要等待某个条件满足时,可以使用 wait 方法。wait 方法通常需要在一个互斥锁(Mutex)保护下被调用,以确保线程安全:
// www.javascriptcn.com code example
mutex = Mutex.new
condition = false
# 模拟一个线程检查条件是否满足
Thread.new do
mutex.synchronize do
# 假设经过一段时间后,条件满足
sleep(2)
condition = true
cv.signal # 通知等待的线程
end
end
# 主线程等待条件满足
mutex.synchronize do
cv.wait(mutex) until condition
puts "条件已经满足!"
end在这个例子中,我们首先创建了一个互斥锁 mutex 和一个 ConditionVariable 对象 cv。然后创建了一个新线程,在这个线程中模拟了一个条件满足的过程。主线程通过调用 cv.wait(mutex) 来等待条件满足。注意这里我们使用了 until 循环来不断尝试等待,直到条件满足为止。
广播所有等待的线程
有时候,你可能希望唤醒所有等待的线程,而不仅仅是第一个。这时可以使用 broadcast 方法:
// www.javascriptcn.com code example
mutex = Mutex.new
threads = []
3.times do |i|
threads << Thread.new(i) do |id|
mutex.synchronize do
puts "#{id} 号线程开始等待..."
cv.wait(mutex)
puts "#{id} 号线程被唤醒!"
end
end
end
# 模拟一段时间后,所有线程都被唤醒
sleep(2)
mutex.synchronize do
puts "唤醒所有等待的线程..."
cv.broadcast
end
threads.each(&:join)在这个示例中,我们创建了三个线程,每个线程都在等待条件满足。主线程在两秒后调用 cv.broadcast 来唤醒所有等待的线程。
ConditionVariable 的注意事项
尽管 ConditionVariable 非常强大,但在使用时也需要注意以下几点:
- 避免死锁:确保在调用
wait方法时持有互斥锁,并且在调用signal或broadcast后释放互斥锁,以避免死锁。 - 合理使用 signal 和 broadcast:根据实际需求选择合适的方法唤醒等待的线程。如果只需要唤醒一个线程,那么使用
signal就足够了;如果需要唤醒所有等待的线程,则应该使用broadcast。 - 处理异常:在使用 ConditionVariable 时,应该适当地处理可能出现的异常情况,比如在
wait调用期间线程被中断的情况。
通过合理地使用 ConditionVariable,我们可以有效地管理线程间的同步和通信,从而构建出更加健壮、高效的并发程序。