#include <iostream>
#include <string>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <chrono>
#include <atomic>
std::mutex m;
std::condition_variable cv;
std::string data;
std::atomic<bool> ready( false );
//bool ready = false;
bool processed = false;
void worker_thread1()
{
// Wait until main() sends data
std::unique_lock<std::mutex> lk( m );
cv.wait( lk, [] {return ready==true;} );
// after the wait, we own the lock.
std::cout << "Worker 1 thread is processing data\n";
data += " after processing";
// Send data back to main()
processed = true;
ready = false;
std::cout << "Worker thread signals data processing completed\n";
// Manual unlocking is done before notifying, to avoid waking up
// the waiting thread only to block again (see notify_one for details)
lk.unlock();
cv.notify_one();
}
void worker_thread2()
{
// Wait until main() sends data
std::unique_lock<std::mutex> lk( m );
//cv.wait(lk, []{return ready==true;});
if( !cv.wait_for( lk, std::chrono::milliseconds( 100 ), [] {return ready==true;} ) ) {
std::cout << "Worker 2 thread gave up\n";
return;
}
// after the wait, we own the lock.
std::cout << "Worker 2 thread is processing data\n";
data += " after processing";
// Send data back to main()
processed = true;
std::cout << "Worker thread signals data processing completed\n";
// Manual unlocking is done before notifying, to avoid waking up
// the waiting thread only to block again (see notify_one for details)
lk.unlock();
cv.notify_one();
}
int main()
{
std::thread worker1( worker_thread1 );
std::thread worker2( worker_thread2 );
data = "Example data";
// send data to the worker thread
{
std::lock_guard<std::mutex> lk( m );
ready = true;
std::cout << "main() signals data ready for processing\n";
}
cv.notify_one();
// wait for the worker
{
std::unique_lock<std::mutex> lk( m );
cv.wait( lk, [] {return processed;} );
}
std::cout << "Back in main(), data = " << data << '\n';
worker1.join();
worker2.join();
}
// with notify_one:
// depending which thread passes through the lock first result will be
//
// main() signals data ready for processing
// Worker 1 thread is processing data
// Worker thread signals data processing completed
// Back in main(), data = Example data after processing
// Worker 2 thread gave up
//
// main() signals data ready for processing
// Worker 2 thread is processing data
// Worker thread signals data processing completed
// Worker 1 thread is processing data
// Worker thread signals data processing completed
// Back in main(), data = Example data after processing after processing
//
// Actualy there is a third outcome possible (but rare) - deadlock
// Exercise: find conditions that lead to a deadlock