3

I'm writing a thread safe queue using C++11 and stl threading. The WaitAndPop method currently looks like the following. I would like to be able to pass something to WaitAndPop that indicates if the calling thread has been asked to stop. WaitAndPop should return true if it waited for and returned an element of the queue, it should return false if the calling thread is being stopped.

    bool WaitAndPop(T& value, std::condition_variable callingThreadStopRequested)
    {
        std::unique_lock<std::mutex> lock(mutex);
        while( queuedTasks.empty() )
        {
            queuedTasksCondition.wait(lock);
        }

        value = queue.front();
        queue.pop_front();
        return true;
    }

Is it possible to code something like this? I'm used to a Win32 WaitForMultipleObjects, but can't find an alternative that works for this case.

Thanks.

I've seen this related question, but it didn't really answer the problem. learning threads on linux

Community
  • 1
  • 1
Scott Langham
  • 58,735
  • 39
  • 131
  • 204
  • 1
    My solution to a POSIX `WaitForMultipleEvents` was using `poll()` and using pipes for the events. I don't think that's going to help you with your condition variables, however... – trojanfoe Mar 28 '13 at 16:20
  • 1
    I would say this is a recommended way as described by Antony Williams in "Implementing a Thread-Safe Queue using Condition Variables"- http://www.justsoftwaresolutions.co.uk/threading/implementing-a-thread-safe-queue-using-condition-variables.html. – SChepurin Mar 28 '13 at 16:42
  • Hmm, yes, I see that. It is a thread safe queue, but also doesn't offer the ability to abandon a wait if a thread needs to be stopped (well, not without spinning which any good threader would want to avoid). – Scott Langham Mar 28 '13 at 16:57
  • Possibly I can push some special value onto the queue in a shutdown scenario which tells WaitAndPop to return false when it pops that value. And I might be able to make it jump to the front of the queue too for quicker termination. – Scott Langham Mar 28 '13 at 17:03
  • @didierc - I mean terminate the thread. – Scott Langham Mar 28 '13 at 17:14
  • yes, I finally understood what you meant, thx – didierc Mar 28 '13 at 17:52
  • 1
    I think you have to mutualize the cond variable on all the things you wish the thread to wait for. It's the easiest way. Otherwise, you could revert to using fs objects and use select/poll, but this is posix, not pure C++, so... There's really no equivalent of `WaitForMultipleObjects` in C++ I think. – didierc Mar 28 '13 at 17:58
  • oh, you can also have the thread control messages piggy back on the `queueTask` queue, that way you only have to wait on one object. You would probably have to change the queue into a priority queue, or add a mechanism to close the queue after a termination message has been posted to it. – didierc Mar 28 '13 at 18:03

1 Answers1

9

If I understand your problem correctly, I would probably do something like this:

 bool WaitAndPop(T& value)
 {
    std::unique_lock<std::mutex> lk(mutex);            

    // Wait until the queue won't be empty OR stop is signaled
    condition.wait(lk, [&] ()
    {
        return (stop || !(myQueue.empty()));
    });

    // Stop was signaled, let's return false
    if (stop) { return false; }

    // An item was pushed into the queue, let's pop it and return true
    value = myQueue.front();
    myQueue.pop_front();

    return true;
}

Here, stop is a global variable like condition and myQueue (I suggest not to use queue as a variable name, since it is also the name of a Standard container adapter). The controlling thread can set stop to true (while holding a lock to mutex) and invoke notifyOne() or notifyAll() on condition.

This way, notify***() on the condition variable is invoked both when a new item is pushed into the queue and when the stop signal is being raised, meaning that a thread waking up after waiting on that condition variable will have to check for what reason it has been awaken and act accordingly.

Andy Prowl
  • 124,023
  • 23
  • 387
  • 451