Condition variables (“cond var” in these notes)
- What they are
- Let’s say one thread wants to wait for another thread to finish something that takes a long time
- To do this with mutexes, thread would have to spin-wait
- Sit in a loop, locking the mutex, checking the shared data, unlocking mutex…
- Very inefficient!
- If we were to remove the sleep calls, those threads would spin and use all the CPU
- Condition variables are a way to solve this.
- When you lock an already-locked mutex, the thread sleeps until the mutex is unlocked.
- Condition variables are a little more general
- They let a thread sleep until some other thread says it’s time to wake up.
- Cond vars give you two operations:
- wait on a cond var, to sleep until the condition is met
- signal a cond var, to wake up a thread waiting on it.
- there’s some weirdness here…
- Like with mutexes and global variables…
- The cond var does not contain the condition itself
- It’s up to you to check the ACTUAL condition (global variable?)
- And it’s up to you to only wake up other threads when an ACTUAL condition is met.
- Cond vars are paired with a mutex!
- The cond var is itself a shared object and access must be synchronized
- But they work with the mutex in an interesting way…
- How they work
- Create a mutex and associated cond var.
- To wait on a cond var:
- Lock the mutex
- Wait on the cond var in association with the mutex
- When the wait finishes, the condition has been signaled
- IMPORTANT: due to complex implementation details, the condition might not have been signaled
- so you have to re-check the actual condition in a loop until it’s true.
- Finally, unlock the mutex
- To signal a cond var:
- Lock the mutex
- Change any shared state, if needed
- Signal/broadcast on the cond var
- Unlock the mutex
- signal means “wake up at least one waiting thread”
- for the reason above.
- only one thread will actually “win” though.
- broadcast means “wake up all threads waiting”
- Why the mutex?
- Well, wait has an interesting behavior
- It atomically unlocks the mutex and blocks the thread.
- It does this to avoid race condition between signal and wait
- Otherwise signal could be sent to a thread that sleeps after the signal is sent.. and it will never wake up!
- Before a thread can wake up when signaled, it reacquires the mutex
- So now you have to unlock it as usual
- From your perspective it looks like the thread just keeps the mutex forever
- But it don’t.
Applications of condition variables
- Very common!
- 1 or more “producers” (something that makes “things to do”)
- 1 or more “consumers” (something that “does the things”)
- Producers put the things to do into a list/queue/whatever (a “todo” box)
- Consumers take things out and handle them
- Consider a web server:
- It sits and waits for someone to connect to it.
- When someone does, it produces a task: “respond to that client.”
- A consumer thread says “ok, I’ll handle it” and responds to the client.
- Typically there are lots of clients coming in at once, so we have lots of consumer (“worker”) threads.
- Barrier synchronization
- You more or less did this in the last lab, but…
- In the lab, you waited for every thread to exit
- With condition variables, you can wait for every thread to get to a certain checkpoint
- And then let them continue on once everyone gets there.
- They’re kinda like a generalized version of a mutex
- With a mutex, either 0 or 1 threads are allowed to use something.
- With a semaphore, 0 through n threads are allowed to use something. (Or a collection of things.)
- Consider a motel or hotel
- Fixed number of rooms
- Only one guest can check in/out at a time
- If all rooms are taken, they turn on their “no vacancy” sign and no one else can come in
- It starts off with a numerical value n - let’s say 10 here
- Every time a thread locks it:
n == 0, the thread sleeps (just like with a mutex).
- Every time a thread unlocks it:
nwas 0, we can now unblock any threads waiting on it.
- You could do this with a mutex/condition variable, but…
- Sometimes it’s just more natural to use a semaphore.
- Just like it’s sometimes more natural to use a cond var instead of a spin-wait.