c++multithreadingcondition-variable

How to prevent worker threads from idling until next condition variable notify


Let's just say I have a bunch of worker threads waiting until there are tasks in the queue.
Generally you have the producer call the produce function, and then notify_one so that the worker thread can unblock and continue.
However let's just say that when the producer produced there are not threads waiting on the condition_variable. The producer will notify, which will do nothing. Then a consumer arrives at the condition_variable and waits, but it missed the previous notification.
Now you have a task in a queue until the producer calls notify AGAIN, at which point there will be two tasks in the queue.

My question is how do I prevent there being jobs in the queue waiting when there are available threads around?

Example:

struct ThreadPool
{
    static inline std::mutex mutex;
    static inline std::condition_variable condition_var;

    static inline int number_of_things_to_consume = 0;

    static void consume()
    {
        std::unique_lock lock{ mutex };

        /* THE WORKER THREAD COULD BE WAITING HERE WITH SOMETHING IN THE QUEUE
        - BECAUSE THE PRODUCER CALLED NOTIFY ONE BUT NO THREAD WAS WAITING YET */
        condition_var.wait(lock, []() {return number_of_things_to_consume > 0; });

        /* CONSUME */
    }

    static void produce()
    {
        std::unique_lock lock{ mutex };
        ++number_of_things_to_consume;
        condition_var.notify_one();
    }
};

Solution

  • My question is how do I prevent there being jobs in the queue waiting when there are available threads around?

    Bottom line:

    Since you used a wait with a predicate your concern is unfounded.
    Your code is OK and there will be no such scenario.

    Some more information:

    As you can see in the documentation, using wait with a predicate like you did is equivalent to:

    while (!pred())
        wait(lock);
    

    So if the predicate is true (because the producer added a job) the consumer will not wait at all.
    And since the producer is always updating the predicate under the lock (as it indeed should), the consumer will not miss it in case it got into the wait.

    A side note:
    Using a predicate instead of a bare wait is also recommended in order to deal with spurious wakeups. So in general is it recommneded to always use a predicate with wait.