Battler
Battler

Reputation: 65

C++ wait notify in threads with synchronized queues

I have a program structured like that: one thread that receives tasks and writes them to input queue, multiple which process them and write in output queue, one that responds with results from it. When queue is empty, thread sleeps for several milliesconds. Queue has mutex inside it, pushing does lock(), and popping does try_lock() and returns if there is nothing in queue.

This is processing thread for example:

//working - atomic bool
while (working) {
    if (!inputQue_->pop(msg)) {
        std::this_thread::sleep_for(std::chrono::milliseconds(200));
        continue;
    } else {
        string reply = messageHandler_->handle(msg);
        if (!reply.empty()) {
            outputQue_->push(reply);
        }
    }
}

And the thing that I dont like is that the time since receiving task until responding, as i have measured with high_resolution_clock, is almost 0, when there is no sleeping. When there is sleeping, it becomes bigger. I dont want cpu resources to be wasted and want to do something like that: when recieving thread gets task, it notifies one of the processing threads, that does wait_for, and when processing task is done, it notifies responding thread same way. As a result I think i will get less time spent and cpu resources will not be wasted. And I have some questions:

  1. Will this work the way that I see it supposed to, and the only difference will be waking up on notifying?
  2. To do this, I have to create 2 condition variables: first same for receiving thread and all processing, second same for all processing and responding? And mutex in processing threads has to be common for all of them or uniuqe?
  3. Can I place creation of unique_lock(mutex) and wait_for() in if branch just instead of sleep_for?
  4. If some processing threads are busy, is it possible that notify_one() can try to wake up one of them, but not the free thread? I need to use notify_all()?
  5. Is it possible that notify will not wake up any of threads? If yes, does it have high probability?

Upvotes: 2

Views: 1978

Answers (3)

Daniel Zin
Daniel Zin

Reputation: 499

In general, your considerations about condition variable are correct. My proposal is more connected to design and reusability of such functionality. The main idea is to implement ThreadPool pattern, which has constructor with number of worker threads ,methods submitTask, shutdown, join. Having such class, you will use 2 instances of pools: one multithreaded for processing, second (singlethreaded by your choice) for result sending. The pool consists of Blocking Queue of Tasks and array of Worker threads, each performing the same "pop Task and run" loop.The Blocking Queue encapsulates mutex and cond_var. The Task is common functor. This also brings your design to Task oriented approach, which has a lot of advantages in future of your application. You are welcome to ask more questions about implementation details if you like this idea. Best regards, Daniel

Upvotes: 0

Humphrey Winnebago
Humphrey Winnebago

Reputation: 1702

The mechanism you have is called polling. The thread repeatedly checks (polls) if there is data available. As you mentioned, it has the drawback of wasting time. (But it is simple). What you mentioned you would like to use is called a blocking mechanism. This deschedules the thread until the moment that work becomes available.

1) Yes (although I don't know exactly what you're imagining)

2) a) Yes, 2 condition variables is one way to do it. b) Common mutex is best

3) You would probably place those within pop, and calling pop would have the potential to block.

4) No. notify_one will only wake a thread that is currently waiting from having called wait. Also, if multiple are waiting, it is not necessarily guaranteed which will receive the notification. (OS/library dependent)

5) No. If 1+ threads are waiting, notify_one it is guaranteed to wake one. BUT if no threads are waiting, the notification is consumed (and has no effect). Note that under certain edge conditions, notify_one may actually wake more than one. Also, a thread may wake from wait without anyone having called notify_one ("Spurious wake up"). The fact that this can happen at all means that you always have to do additional checking for it.

This is called the producer/consumer problem btw.

Upvotes: 1

David Schwartz
David Schwartz

Reputation: 182893

Will this work the way that I see it supposed to, and the only difference will be waking up on notifying?

Yes, assuming you do it correctly.

To do this, I have to create 2 condition variables: first same for receiving thread and all processing, second same for all processing and responding? And mutex in processing threads has to be common for all of them or uniuqe?

You can use a single mutex and a single condition variable, but that makes it a bit more complex. I'd suggest a single mutex, but one condition variable for each condition a thread might want to wait for.

Can I place creation of unique_lock(mutex) and wait_for() in if branch just instead of sleep_for?

Absolutely not. You need to hold the mutex while you check whether the queue is empty and continue to hold it until you call wait_for. Otherwise, you destroy the entire logic of the condition variable. The mutex associated with the condition variable must protect the condition that the thread is going to wait for, which in this case is the queue being non-empty.

If some processing threads are busy, is it possible that notify_one() can try to wake up one of them, but not the free thread? I need to use notify_all()?

I don't know what you mean by the "free thread". As a general rule, you can use notify_one if it's not possible for a thread to be blocked on the condition variable that can't handle the condition. You should use notify_all if either more than one thread might need to be awoken or there's a possibility that more than one thread will be blocked on the condition variable and the "wrong thread" could be woken, that is, there could be at least one thread that can't do whatever it is that needs to be done.

Is it possible that notify will not wake up any of threads? If yes, does it have high probability?

Sure, it's quite possible. But that would mean no threads were blocked on the condition. In that case, no thread can block on the condition because threads must check the condition before they wait, and they do it while holding a mutex. To provide this atomic "unlock and wait" semantic is the entire purpose of a condition variable.

Upvotes: 1

Related Questions