Simple plain C ++ producer problem
I have really simple code for streaming consumer C ++ code, except I am typing "Hello World"
I want the following printable code to be:
"HelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorld"
condition_variable mcond; // the variable communicating events
mutex mmutex; // the locking mechanism
void hello()
{
while (true) {
unique_lock<mutex> lck{ mmutex }; // acquire mmutex
mcond.wait(lck); /* do nothing */; // release lck and wait;
// re-acquire lck upon wakeup
cout << "Hello";
lck.unlock();
}
}
void world()
{
while (true)
{
unique_lock<mutex> lck{ mmutex }; // protect operations
cout << "World";
mcond.notify_one();
}
}
int main()
{
thread t1{ hello };
thread t2{ world };
t1.join();
t2.join();
return 0;
}
What is the above code:
"WorldWorldWorldWorldWorldWorldWorldWorldWorldWorld Hello WorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorld Hello WorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldHelloWorldWorldWorldWorldWorld"
I suspect that the reason it doesn't print in sync is because the world () function releases the lock and immediately grabs it before hello () has a chance to grab it.
I have no idea how to solve this problem. How to properly configure thread synchronization?
source to share
Threads are inherently unsynchronized, so it is not known how long one thread will run before another gets a chance for a CPU
time.
If you need a strict order for your outlet, you will have to do so. One way is with a flag to tell each thread which word to print like this:
// avoid using global information
struct hello_world_control
{
std::condition_variable cv;
std::mutex mtx;
bool do_hello = true; // start with "Hello"
// make this atomic to avoid having to lock it
std::atomic_bool done{false};
};
void hello(hello_world_control& ctrl)
{
// setting ctrl.done = true; will end the thread
while(!ctrl.done)
{
// start a new scope for the lock
{
std::unique_lock<std::mutex> lock{ctrl.mtx};
// wait until do_hello become true
ctrl.cv.wait(lock, [&]{ return ctrl.do_hello; });
std::cout << " Hello";
ctrl.do_hello = false; // signal hello has been done
}
// when the scope ends the lock is released
ctrl.cv.notify_one(); // tell the waiting thread
}
}
void world(hello_world_control& ctrl)
{
while(!ctrl.done)
{
{
std::unique_lock<std::mutex> lock{ctrl.mtx};
// wait until do_hello become false
ctrl.cv.wait(lock, [&]{ return !ctrl.do_hello; });
std::cout << " World";
ctrl.do_hello = true; // signal hello now needs to happen
}
ctrl.cv.notify_one(); // tell the waiting thread
}
}
int main()
{
hello_world_control ctrl;
// use std::ref() to pass ctrl by reference to each thread
std::thread t1{hello, std::ref(ctrl)};
std::thread t2{world, std::ref(ctrl)};
// let threads run for 100 milliseconds
std::this_thread::sleep_for(std::chrono::milliseconds(100));
// signal threads to stop looping
ctrl.done = true;
// synchronize with main thread
t1.join();
t2.join();
}
Output:
Hello World Hello World Hello World
Hello World Hello World Hello World
Hello World Hello World Hello World
...
source to share