Class: Thread::ConditionVariable
| Relationships & Source Files | |
| Inherits: | Object |
| Defined in: | thread_sync.c, thread_sync.c, thread_sync.rb |
Overview
ConditionVariable objects augment class Mutex. Using condition variables, it is possible to suspend while in the middle of a critical section until a condition is met, such as a resource becomes available.
Due to non-deterministic scheduling and spurious wake-ups, users of condition variables should always use a separate boolean predicate (such as reading from a boolean variable) to check if the condition is actually met before starting to wait, and should wait in a loop, re-checking the condition every time the ConditionVariable is waken up. The idiomatic way of using condition variables is calling the #wait method in an until loop with the predicate as the loop condition.
condvar.wait(mutex) until condition_is_met
In the example below, we use the boolean variable resource_available (which is protected by mutex) to indicate the availability of the resource, and use condvar to wait for that variable to become true. Note that:
-
::Threadbmay be scheduled before threada1anda2, and may run so fast that it have already made the resource available before eithera1ora2starts. Therefore,a1anda2should check ifresource_availableis already true before starting to wait. -
The #wait method may spuriously wake up without signalling. Therefore, thread
a1anda2should recheckresource_availableafter the #wait method returns, and go back to wait if the condition is not actually met. -
It is possible that thread
a2starts right after threada1is waken up byb. Threada2may have acquired themutexand consumed the resource before threada1acquires themutex. This necessitates rechecking after #wait, too.
Example:
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}
Class Method Summary
-
.new ⇒ ConditionVariable
constructor
Document-method: .new
Instance Method Summary
-
#broadcast
Document-method:
broadcast. -
#signal
Document-method:
signal. -
#wait(mutex, timeout = nil)
Document-method:
wait. - #marshal_dump Internal use only
Constructor Details
.new ⇒ ConditionVariable
Document-method: .new
Creates a new condition variable instance.
# File 'thread_sync.rb', line 156
def initialize end
Instance Method Details
#broadcast
Document-method: broadcast
Wakes up all threads waiting for this lock.
# File 'thread_sync.rb', line 176
def broadcast Primitive.rb_condvar_broadcast end
#marshal_dump
#signal
Document-method: signal
Wakes up the first thread in line waiting for this lock.
# File 'thread_sync.rb', line 169
def signal Primitive.rb_condvar_signal end
#wait(mutex, timeout = nil)
Document-method: wait
Releases the lock held in mutex and waits; reacquires the lock on wakeup.
If timeout is given, this method returns after timeout seconds passed, even if no other thread doesn’t signal.
This method may wake up spuriously due to underlying implementation details.
Returns the slept result on mutex.
# File 'thread_sync.rb', line 191
def wait(mutex, timeout=nil) Primitive.rb_condvar_wait(mutex, timeout) end