![Coders School](/coders-school/condition-variable/raw/master/coders_school_logo.png)
template <typename T>
class WaitQueue {
deque<T> queue_;
mutable mutex m_;
condition_variable nonEmpty_;
using Lock = unique_lock<mutex>;
public:
void push(const T & element) {
{
Lock l(m_);
queue_.push_front(element);
}
nonEmpty_.notify_all();
}
T pop() {
Lock l(m_);
auto hasData = [&]{ return not queue_.empty(); };
nonEmpty_.wait(l, hasData);
auto top = queue_.back();
queue_.pop_back();
return top;
}
};
Producer
Queue
Element 1
Condvar
Mutex
Consumer A
Consumer B
template <typename T>
class WaitQueue {
deque<T> queue_;
mutable mutex m_;
condition_variable nonEmpty_;
using Lock = unique_lock<mutex>;
public:
void push(const T & element) {
{
Lock l(m_);
queue_.push_front(element);
}
nonEmpty_.notify_all();
}
T pop() {
Lock l(m_);
auto hasData = [&]{ return not queue_.empty(); };
nonEmpty_.wait(l, hasData);
auto top = queue_.back();
queue_.pop_back();
return top;
}
};
Producer
Queue
Element 1
Condvar
Mutex
Consumer A
Consumer B
template <typename T>
class WaitQueue {
deque<T> queue_;
mutable mutex m_;
condition_variable nonEmpty_;
using Lock = unique_lock<mutex>;
public:
void push(const T & element) {
{
Lock l(m_);
queue_.push_front(element);
}
nonEmpty_.notify_all();
}
T pop() {
Lock l(m_);
auto hasData = [&]{ return not queue_.empty(); };
nonEmpty_.wait(l, hasData);
auto top = queue_.back();
queue_.pop_back();
return top;
}
};
Producer
Queue
Element 1
Condvar
Consumer A
Mutex
Consumer B
template <typename T>
class WaitQueue {
deque<T> queue_;
mutable mutex m_;
condition_variable nonEmpty_;
using Lock = unique_lock<mutex>;
public:
void push(const T & element) {
{
Lock l(m_);
queue_.push_front(element);
}
nonEmpty_.notify_all();
}
T pop() {
Lock l(m_);
auto hasData = [&]{ return not queue_.empty(); };
nonEmpty_.wait(l, hasData);
auto top = queue_.back();
queue_.pop_back();
return top;
}
};
Producer
Queue
Condvar
Consumer A
Mutex
Element 1
Consumer B
template <typename T>
class WaitQueue {
deque<T> queue_;
mutable mutex m_;
condition_variable nonEmpty_;
using Lock = unique_lock<mutex>;
public:
void push(const T & element) {
{
Lock l(m_);
queue_.push_front(element);
}
nonEmpty_.notify_all();
}
T pop() {
Lock l(m_);
auto hasData = [&]{ return not queue_.empty(); };
nonEmpty_.wait(l, hasData);
auto top = queue_.back();
queue_.pop_back();
return top;
}
};
Producer
Queue
Condvar
Mutex
Consumer A
Element 1
Consumer B
template <typename T>
class WaitQueue {
deque<T> queue_;
mutable mutex m_;
condition_variable nonEmpty_;
using Lock = unique_lock<mutex>;
public:
void push(const T & element) {
{
Lock l(m_);
queue_.push_front(element);
}
nonEmpty_.notify_all();
}
T pop() {
Lock l(m_);
nonEmpty_.wait(l);
auto top = queue_.back();
queue_.pop_back();
return top;
}
};
Producer
Queue
Condvar
Mutex
Consumer A
Element 1
Consumer B
template <typename T>
class WaitQueue {
deque<T> queue_;
mutable mutex m_;
condition_variable nonEmpty_;
using Lock = unique_lock<mutex>;
public:
void push(const T & element) {
{
Lock l(m_);
queue_.push_front(element);
}
nonEmpty_.notify_all();
}
T pop() {
Lock l(m_);
nonEmpty_.wait(l);
auto top = queue_.back();
queue_.pop_back();
return top;
}
};
Producer
Queue
Condvar
Consumer A
Element 1
Consumer B
Mutex
template <typename T>
class WaitQueue {
deque<T> queue_;
mutable mutex m_;
condition_variable nonEmpty_;
using Lock = unique_lock<mutex>;
public:
void push(const T & element) {
{
Lock l(m_);
queue_.push_front(element);
}
nonEmpty_.notify_all();
}
T pop() {
Lock l(m_);
nonEmpty_.wait(l);
auto top = queue_.back();
queue_.pop_back();
return top;
}
};
template <typename T>
class WaitQueue {
deque<T> queue_;
mutable mutex m_;
condition_variable nonEmpty_;
using Lock = unique_lock<mutex>;
public:
void push(const T & element) {
{
Lock l(m_);
queue_.push_front(element);
}
nonEmpty_.notify_all();
}
T pop() {
Lock l(m_);
nonEmpty_.wait(l);
auto top = queue_.back();
queue_.pop_back();
return top;
}
};
-
The thread waiting on the
condition_variable
periodically wakes up from time to time. -
A
unique_lock
is required to wait on a condition variable. During sleep, the thread unlocks it, and when it wakes up, it locks it again. -
Predicate added to the
wait()
function makes spurious wake-ups not dangerous.- Only when the condition is met, the thread will continue its work.
- If the condition is not met, the thread releases the lock and goes to sleep again.
-
ALWAYS use
wait()
with predicate to avoid potential problems.