Using condition variables
A few posts ago, we’ve seen how we could create a producer/consumer stack. Let’s update the example for using condition variables. Here’s the code:
class ConcurrentStack<T> {
private readonly Object _locker = new Object();
private readonly Stack<T> _stack = new Stack<T>();
public void Push(T element) {
lock (_locker) {
_stack.Push(element);
Monitor.Pulse(_locker);
}
}
public T Pop() {
lock (_locker) {
while (_stack.Count == 0) {
Monitor.Wait(_locker);
}
return _stack.Pop();
}
}
}
The first thing you should keep in mind is that you can only call Pulse or Wait (or PulseAll) when the calling thread owns the lock for the object. Not doing that means that you’ll get a SynchronizationLockException exception. The most interesting part of the code is the while cycle that wraps the Monitor.Wait call. It’s there because when a thread is awaken in a condition, it won’t run immediately (so there’s always a chance that another thread might have been scheduled to run and has already pop the stack into an empty state).
And that’s all for today. In the next post, we’re going to explore our options for creating new threads (until now, we’ve been creating new threads explicitly).