c++ condition variable notification not working as expected
Asked Answered
S

1

12

I'm trying to launch new threads as soon as work in previous worker_thread has started, but maybe ended or not. I've replaced started and ended work with time delays. My code is:

#include <iostream>
#include <string>
#include <mutex>
#include <condition_variable>
#include <future>
#include <atomic>
#include <chrono>
#include <thread>

std::mutex m;
std::condition_variable cv;
bool started = false;

void worker_thread()
{
    std::unique_lock<std::mutex> lk(m);

    static std::atomic<int> count(1);
    std::this_thread::sleep_for(std::chrono::milliseconds{(count % 5) * 100});
    std::cerr << "Start Worker thread: " << count << "\n";

    started = true;
    lk.unlock();
    cv.notify_one();

    std::this_thread::sleep_for(std::chrono::milliseconds{3000});
    std::cerr << "Exit Worker thread: " << count << "\n";
    ++count;
}

int main()
{
    while(1) {
        std::async(std::launch::async, worker_thread);
        std::unique_lock<std::mutex> lk(m);
        cv.wait(lk, []{return started;});
        started = false;
    }
}

The output looks like that:

Start Worker thread: 1
Exit Worker thread: 1
Start Worker thread: 2
Exit Worker thread: 2
Start Worker thread: 3
Exit Worker thread: 3
Start Worker thread: 4
Exit Worker thread: 4
Start Worker thread: 5
Exit Worker thread: 5

which isn't the behavior I wanted. What I wanted was something like (not exactly) this:

Start Worker thread: 1
Start Worker thread: 2
Start Worker thread: 3
Start Worker thread: 4
Exit Worker thread: 1
Exit Worker thread: 3
Exit Worker thread: 4
Exit Worker thread: 2
Start Worker thread: 5
Exit Worker thread: 5

Currently the next thread is only started when work is finished in previous thread. But I want to start next thread as soon as work is started in previous thread and not wait for it's end, only wait for start.

Sthenic answered 1/4, 2017 at 12:32 Comment(0)
D
11

std::async returns a std::future holding result of function execution. In your case, it is a temporary object which is immideatly destroyed. The documentation for std::future says:

these actions will not block for the shared state to become ready, except that it may block if all of the following are true:

✔ the shared state was created by a call to std::async

✔ the shared state is not yet ready

✔ this was the last reference to the shared state

All of those are true, so destruction of that future will block until worker function will finish executing.

You can create detached thread to avoid this problem:

std::thread(worker_thread).detach();
Diverting answered 1/4, 2017 at 12:49 Comment(4)
There are still other problems: If cv.notify_one is called before cv.wait is running the notification will be missed. In the presented code it most probably will. The variable 'started' is set to true by the first worker_thread and remains in this state.Reluctant
@Reluctant I forgot to write started = false at end of while loop, have updated the code. But I do not understand how to solve missing notification problem, I cannot wait random amount of time since start event can take any amount of time..Sthenic
@Laser Focus: If you switch the std::async and the std::unique_lock statements before the cv.wait, the wait will definitly happen before the notify_one.Reluctant
@Laser Focus: The wait on the condition variable releases the lock making the thread run...Reluctant

© 2022 - 2024 — McMap. All rights reserved.