class Thread::ConditionVariable

ConditionVariable 对象是对类 Mutex 的一个补充。使用条件变量,可以在临界区内暂停,直到满足某个条件,例如资源可用。

由于非确定性调度和虚假唤醒,条件变量的用户在使用前,应始终使用一个单独的布尔谓词(例如读取布尔变量)来检查条件是否实际满足,并且应该在一个循环中等待,每次 ConditionVariable 被唤醒时重新检查条件。使用条件变量的惯用方法是在一个 until 循环中调用 wait 方法,并将谓词作为循环条件。

condvar.wait(mutex) until condition_is_met

在下面的示例中,我们使用布尔变量 resource_available(受 mutex 保护)来指示资源的可用性,并使用 condvar 来等待该变量变为 true。请注意,

  1. Thread b 可能在线程 a1a2 之前被调度,并且可能运行得非常快,以至于在 a1a2 开始之前就已经使资源可用。因此,a1a2 在开始等待之前,应该检查 resource_available 是否已经为 true。

  2. wait 方法可能会在没有收到信号的情况下发生虚假唤醒。因此,线程 a1a2wait 方法返回后,应该重新检查 resource_available,如果条件未实际满足,则返回等待。

  3. 线程 a2 可能在线程 a1b 唤醒后立即开始。线程 a2 可能在线程 a1 获得 mutex 之前就获得了 mutex 并消耗了资源。这使得在 wait 之后也需要重新检查。

示例

mutex = Thread::Mutex.new

resource_available = false
condvar = Thread::ConditionVariable.new

a1 = Thread.new {
  # Thread 'a1' waits for the resource to become available and consumes
  # the resource.
  mutex.synchronize {
    condvar.wait(mutex) until resource_available
    # After the loop, 'resource_available' is guaranteed to be true.

    resource_available = false
    puts "a1 consumed the resource"
  }
}

a2 = Thread.new {
  # Thread 'a2' behaves like 'a1'.
  mutex.synchronize {
    condvar.wait(mutex) until resource_available
    resource_available = false
    puts "a2 consumed the resource"
  }
}

b = Thread.new {
  # Thread 'b' periodically makes the resource available.
  loop {
    mutex.synchronize {
      resource_available = true

      # Notify one waiting thread if any.  It is possible that neither
      # 'a1' nor 'a2 is waiting on 'condvar' at this moment.  That's OK.
      condvar.signal
    }
    sleep 1
  }
}

# Eventually both 'a1' and 'a2' will have their resources, albeit in an
# unspecified order.
[a1, a2].each {|th| th.join}