What happens in this piece of code?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
|
#include <iostream>
#include <string>
#include <thread>
#include <mutex>
#include <condition_variable>
std::mutex m;
std::condition_variable cv;
std::string data;
bool ready = false;
bool processed = false;
void worker_thread()
{
// Wait until main() sends data
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, []{return ready;});
// after the wait, we own the lock.
std::cout << "Worker 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 worker(worker_thread);
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';
worker.join();
}
| |
My expectation is this way, albeit I'm almost sure it's not completely correct:
The program, as useful, starts from the
main() function/thread, the
worker thread is the called on line 35. After that point, the program's control goes to the
worker_thread function. A
unique_lock is created there and the condition variable
cv is going to wait (some situation like sleeping until an event wakes it up) on line 17. So the program's control gets back to
main() on line 37 where it adds a string into
data. Inside the following local block, a
lock_guard is defined and the bool value
ready gets
true. We then have some data as output by
std::cout. By exiting the block, on line 43, the lock on the mutex,
lk, is unlocked automatically.
cv will send a
notify_one signal, what
cv.
wait on line 17 is waiting for, therefore control goes there and we have some output on line 20 followed by altering
data on the next line. Moving on,
processed gets
true and we cout a message and next manually unclock the
lk (on line 29). The control's still there, hence by
cv.notify_one, it goes back to line 46 from the line 30. Afterwards, there's another local block (47-50), in which
lk(m) is defined and
cv falls into a wait. But since
processed is already trued, the control goes out of the block and cout a message on line 51. Thereafter, the
worker thread is finished followed by the main thread. And done!
How much right, please?