C++11 thread-safe queue C++11 thread-safe queue multithreading multithreading

C++11 thread-safe queue


It is best to make the condition (monitored by your condition variable) the inverse condition of a while-loop:while(!some_condition). Inside this loop, you go to sleep if your condition fails, triggering the body of the loop.

This way, if your thread is awoken--possibly spuriously--your loop will still check the condition before proceeding. Think of the condition as the state of interest, and think of the condition variable as more of a signal from the system that this state might be ready. The loop will do the heavy lifting of actually confirming that it's true, and going to sleep if it's not.

I just wrote a template for an async queue, hope this helps. Here, q.empty() is the inverse condition of what we want: for the queue to have something in it. So it serves as the check for the while loop.

#ifndef SAFE_QUEUE#define SAFE_QUEUE#include <queue>#include <mutex>#include <condition_variable>// A threadsafe-queue.template <class T>class SafeQueue{public:  SafeQueue(void)    : q()    , m()    , c()  {}  ~SafeQueue(void)  {}  // Add an element to the queue.  void enqueue(T t)  {    std::lock_guard<std::mutex> lock(m);    q.push(t);    c.notify_one();  }  // Get the "front"-element.  // If the queue is empty, wait till a element is avaiable.  T dequeue(void)  {    std::unique_lock<std::mutex> lock(m);    while(q.empty())    {      // release lock as long as the wait and reaquire it afterwards.      c.wait(lock);    }    T val = q.front();    q.pop();    return val;  }private:  std::queue<T> q;  mutable std::mutex m;  std::condition_variable c;};#endif


According to the standard condition_variables are allowed to wakeup spuriously, even if the event hasn't occured. In case of a spurious wakeup it will return cv_status::no_timeout (since it woke up instead of timing out), even though it hasn't been notified. The correct solution for this is of course to check if the wakeup was actually legit before proceding.

The details are specified in the standard §30.5.1 [thread.condition.condvar]:

—The function will unblock when signaled by a call to notify_one(), a call to notify_all(), expiration of the absolute timeout (30.2.4) specified by abs_time, or spuriously.

...

Returns: cv_status::timeout if the absolute timeout (30.2.4) specifiedby abs_time expired, other-ise cv_status::no_timeout.


This is probably how you should do it:

void push(std::string&& filename){    {        std::lock_guard<std::mutex> lock(qMutex);        q.push(std::move(filename));    }    populatedNotifier.notify_one();}bool try_pop(std::string& filename, std::chrono::milliseconds timeout){    std::unique_lock<std::mutex> lock(qMutex);    if(!populatedNotifier.wait_for(lock, timeout, [this] { return !q.empty(); }))        return false;    filename = std::move(q.front());    q.pop();    return true;    }