class Thread::ConditionVariable

ConditionVariable-Objekte erweitern die Klasse Mutex. Mit Bedingungsvariablen ist es möglich, während eines kritischen Abschnitts zu pausieren, bis eine Bedingung erfüllt ist, z. B. bis eine Ressource verfügbar wird.

Aufgrund der nicht-deterministischen Zeitplanung und falscher Wecksignale sollten Benutzer von Bedingungsvariablen immer ein separates boolesches Prädikat (wie das Lesen aus einer booleschen Variable) verwenden, um zu prüfen, ob die Bedingung tatsächlich erfüllt ist, bevor sie mit dem Warten beginnen. Sie sollten in einer Schleife warten und die Bedingung jedes Mal neu prüfen, wenn die ConditionVariable geweckt wird. Der idiomatische Weg, Bedingungsvariablen zu verwenden, ist der Aufruf der wait-Methode in einer until-Schleife mit dem Prädikat als Schleifenbedingung.

condvar.wait(mutex) until condition_is_met

Im folgenden Beispiel verwenden wir die boolesche Variable resource_available (die durch mutex geschützt wird), um die Verfügbarkeit der Ressource anzuzeigen, und verwenden condvar, um auf das Wahrwerden dieser Variablen zu warten. Beachten Sie, dass

  1. Thread b vor den Threads a1 und a2 geplant werden kann und so schnell laufen kann, dass er die Ressource bereits verfügbar gemacht hat, bevor entweder a1 oder a2 startet. Daher sollten a1 und a2 prüfen, ob resource_available bereits wahr ist, bevor sie mit dem Warten beginnen.

  2. Die wait-Methode kann ohne Signal geweckt werden. Daher sollten die Threads a1 und a2 resource_available erneut prüfen, nachdem die wait-Methode zurückgekehrt ist, und wieder warten, wenn die Bedingung tatsächlich nicht erfüllt ist.

  3. Es ist möglich, dass Thread a2 direkt nachdem Thread a1 von b geweckt wurde, startet. Thread a2 hat möglicherweise den mutex erworben und die Ressource verbraucht, bevor Thread a1 den mutex erwirbt. Dies erfordert auch nach wait eine erneute Prüfung.

Beispiel

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}