0

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();
    }
};
11
  • 3
    This is where the predicate comes in. If it is true, it will fall true (even without notification) Commented Oct 23, 2024 at 6:30
  • "THE WORKER THREAD COULD BE WAITING HERE WITH SOMETHING IN THE QUEUE" - I don't think so. If number_of_things_to_consume is not 0, then wait returns immediately. Commented Oct 23, 2024 at 6:33
  • @PepijnKramer Wait, I thought what "wait()" does is it unlocks the mutex and waits on the condition variable. IF the condition variable is signalled it THEN checks the predicate to decide whether it lock again and enter. The predicate isn't checked beforehand. Commented Oct 23, 2024 at 6:37
  • No it takes the lock, checks the predicate and if its true it won't wait. See : en.cppreference.com/w/cpp/thread/condition_variable/wait (case 2), When using standard library calls, ALWAYS read the documentation first :) Commented Oct 23, 2024 at 6:54
  • 1
    There is this guideline : never use wait without a predicate. It solves this issue but also avoid problems with spurious wakeups (they happen at the OS level and C++ runtime cannot prevent them) Commented Oct 23, 2024 at 8:26

1 Answer 1

2

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.

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.