Could someone explain why this code does not work as expected when the sleep call in producer()
is uncommented? It works fine if the sleep call is moved out of the scope of the unique_lock
.
To clarify: Working as expected: producer()
creates new messages that are stored in data
those messages are then printed by consumer()
. What happens when the sleep call is uncommented: no output is produced. I would expect new messages to be produced and printed at 1 second intervals.
#include <atomic>
#include <condition_variable>
#include <iostream>
#include <mutex>
#include <ratio>
#include <string>
#include <thread>
#include <queue>
std::string data;
std::atomic<bool> ready;
std::condition_variable cv;
std::mutex m;
void producer()
{
int c{};
while (true)
{
++c;
{
std::unique_lock<std::mutex> lock{ m };
data = "count: " + std::to_string(c);
// std::this_thread::sleep_for(std::chrono::seconds{ 1 }); // THIS LINE
ready = true;
}
cv.notify_one();
}
}
void consumer()
{
while (true)
{
std::unique_lock<std::mutex> lock{ m };
cv.wait(lock, []{ return ready == true; });
std::cout << data << '\n';
ready = false;
}
}
int main()
{
std::thread tp{ producer };
std::thread tc{ consumer };
tp.join();
tc.join();
}
Each iteration of the while
loop in the producer
thread takes a lock, computes data
, sets ready=true
, and then unlocks before repeating.
In the case of the sleep statement being uncommented out:
Since the mutex is still held by the OS while sleep
is invoked, both the producer and consumer thread are effectively paused and are not being given any quantum to run.
Then the sleep statement completes. This enables the producer thread to wake up (with a full quantum). The producer thread, within the quantum time likely is attempting to do the following:
ready=true
notify_one
to signal the consumer thread that it can get a shot. But keep in mind, the consumer thread is likely paused at this point while the producer thread is running.Meanwhile the consumer thread is blocked on the wait
call. But it eventually gets signaled as available to run when the notify_one
call is invoked by the producer thread. Then this likely happens:
The consumer thread will get woken up during step 3 of the producer thread above. The thread's register state and other stuff will get restored by the OS. This takes a few cycles. Then it attempts to acquire the lock itself. But by the time it trys to acquire the lock, the producer thread has grabbed it again, because it's already running and not blocked on anything.
Now, I ran your program with the sleep statement uncommented. It will occassionally print a count: XX
line, but rarely. There's a small window in which the consumer thread can "win" the race with the producer thread and acquire the mutex. But it will take many iterations and some luck. In my local repro of building and running your original code with the sleep
statement uncommented, the program printed this line exactly and nothing else after running for several minutes.
count: 41
It just happens to work when the sleep statement is commented out because the thread scheduler just happens to occasionally context switch between threads in between unlock and lock calls to the mutex. You might even have different behavior depending on the number of available cores on your system. As evidence of this, you can see that the consumer
thread winds up skipping over a bunch of values on each consecutive print call. That's because the producer thread isn't ever blocked on i/o like the consumer thread is with the cout
and wait
statements.
count: 131
count: 134
count: 2661
count: 2804
count: 2880
...
I'm guessing you want to have the consumer thread print each new value assigned to data. If that's the case, unlock the mutex while sleeping. We can unlock and relock the mutex between sleep statements. But just swapping some lines will suffice:
void producer()
{
int c{};
while (true)
{
++c;
{
std::unique_lock<std::mutex> lock{ m };
data = "count: " + std::to_string(c);
ready = true;
}
cv.notify_one();
std::this_thread::sleep_for(std::chrono::seconds{ 1 });
}
}
This will result in consumer
printing out each unique value of data
instead of skipping over any:
count: 1
count: 2
count: 3
count: 4
count: 5
count: 6
Collected from the Internet
Please contact [email protected] to delete if infringement.
Comments