Condition variables
A mutex stops two threads running the same code at once. A condition variable lets a thread sleep until another thread tells it something has changed, instead of checking over and over.
#cmdline "-mt"
'' A one-slot mailbox: the producer waits while it is full, the
'' consumer waits while it is empty.
type Mailbox
as any ptr lock, changed
as boolean full
as int32 item
end type
sub Producer( byval p as any ptr )
var box = cptr( Mailbox ptr, p )
for n as int32 = 1 to 5
mutexlock( box->lock )
do while box->full '' re-test after every wake-up
condwait( box->changed, box->lock )
loop
box->item = n * 10
box->full = true
condsignal( box->changed )
mutexunlock( box->lock )
next
end sub
dim box as Mailbox
box.lock = mutexcreate( )
box.changed = condcreate( )
var worker = threadcreate( @Producer, @box )
for k as int32 = 1 to 5
mutexlock( box.lock )
do until box.full '' sleeps, releasing the lock
condwait( box.changed, box.lock )
loop
print "took"; box.item
box.full = false
condsignal( box.changed )
mutexunlock( box.lock )
next
threadwait( worker )
conddestroy( box.changed )
mutexdestroy( box.lock )
Output
took 10
took 20
took 30
took 40
took 50
The mailbox holds one item. The producer thread waits while it is full; the
main program waits while it is empty. Each side changes full with the mutex
held and then calls condsignal, which wakes a thread sleeping on changed.
The five items arrive in order because neither side can get ahead of the
other.
condwait( cond, mutex ) does three things: it releases the mutex, sleeps
until it is signalled, and takes the mutex back before it returns. Releasing
the mutex is what lets the other thread change full in the meantime.
Always test the condition in a loop, as both do while box->full and
do until box.full do. A wake-up says only that something may have changed:
the thread can be woken with nothing to do, or find that another thread has
already taken what it was woken for.
| Call | Does |
|---|---|
condcreate( ), conddestroy( c ) | make and free a condition variable |
condwait( c, m ) | release m, sleep until signalled, retake m |
condsignal( c ) | wake one thread waiting on c |
condbroadcast( c ) | wake every thread waiting on c |
A channel with a capacity of 1 does the same job as this mailbox in a few lines. Reach for a condition variable when the condition is more than "a value is waiting".
Activities
- Make the mailbox hold up to three items, with a count instead of
full. - Start two consumer threads and use
condbroadcastwhen the producer finishes.