Reputation: 13
I am slightly confused in mutex, lock, and wait.
Here is my code:
void producer(std::mutex* m, std::condition_variable* cv,bool* signal) {
std::this_thread::sleep_for(std::chrono::seconds(1));
m->lock();
*signal = true;
m->unlock();
std::this_thread::sleep_for(std::chrono::seconds(3));
cv->notify_one();
printf("notify one\n");
std::this_thread::sleep_for(std::chrono::seconds(10000));
}
void consumer(std::mutex*m, std::condition_variable* cv, bool* signal, int index) {
if(index == 2) std::this_thread::sleep_for(std::chrono::seconds(2));
std::unique_lock<std::mutex> lk(*m);
cv->wait(lk, [&] { return (*signal); });
printf("consumer %d passes this point!\n", index);
std::this_thread::sleep_for(std::chrono::seconds(10000));
}
int main() {
bool signal = false;
std::mutex m;
std::condition_variable cv;
std::thread th1(producer, &m, &cv, &signal);
std::thread th2(consumer, &m, &cv, &signal, 1);
std::thread th3(consumer, &m, &cv, &signal, 2);
th1.join();
th2.join();
th3.join();
return 0;
}
std::this_thread::sleep_for
is added to explain my question.
There are producer, consumer1, and consumer 2. I think that this code should work as follows:
std::unique_lock<std::mutex> lk(*m);
so it locks.cv->wait
. Because initial value of signal
is false
, consumer1 is blocked and the lock is released.m->lock();
, *signal = true;
, m->unlock();
, and sleep_for
. Therefore, signal
becomes true
.std::unique_lock<std::mutex> lk(*m);
and cv->wait(lk, [&] { return (*signal); });
. Becuase signal
is true
, this thread just passes it. So, printf("consumer %d passes this point!\n", index);
is executed.cv->notify_one();
. consumer 1 is unblocked and check the condition. Because signal
is ture
, consumer1 can pass this point. Therefore, consumer1 meets printf
.Consequently, my expected result is
consumer 2 passes this point!
notify one
consumer 1 passes this point!
However, the real result is
consumer 2 passes this point!
notify one
It seems to be that consumer1 cannot pass cv->wait(lk, [&] { return (*signal); });
, even though notify_one()
is called and the condition is satisfied. Is there anything wrong in my understanding?
Upvotes: 1
Views: 164
Reputation: 6484
The problem is consumer 2 doesn't release the lock on the mutex before going to sleep:
std::unique_lock<std::mutex> lk(*m);
cv->wait(lk, [&] { return (*signal); });
printf("consumer %d passes this point!\n", index); // <-- mutex is still locked here
std::this_thread::sleep_for(std::chrono::seconds(10000));
So even though the condition in consumer 1 may be satisfied, it cannot acquire the lock on the mutex because it's already locked by another thread.
An std::unique_lock gives you more fine grained control than its counterpart std::lock_guard
including the ability to unlock. What you can do is add an unlock call before the consumer goes to sleep like this:
...
lk.unlock();
std::this_thread::sleep_for(std::chrono::seconds(10000));
Otherwise the lock on the mutex is only released after consumer 2 has completed executing the consumer()
function, i.e. after the long sleep.
Upvotes: 1
Reputation: 1290
You are not releasing lock in consumer
routine.
The code below does what is expected:
#include <thread>
#include <chrono>
#include <condition_variable>
#include <mutex>
void producer(std::mutex* m, std::condition_variable* cv,bool* signal) {
std::this_thread::sleep_for(std::chrono::seconds(1));
{
std::unique_lock<std::mutex> lk(*m);
*signal = true;
}
std::this_thread::sleep_for(std::chrono::seconds(3));
cv->notify_all();
printf("notify one\n");
std::this_thread::sleep_for(std::chrono::seconds(5));
}
void consumer(std::mutex*m, std::condition_variable* cv, bool* signal, int index) {
if(index == 2) std::this_thread::sleep_for(std::chrono::seconds(2));
{
std::unique_lock<std::mutex> lk(*m);
cv->wait(lk, [&] { return (*signal); });
}
printf("consumer %d passes this point!\n", index);
std::this_thread::sleep_for(std::chrono::seconds(5));
}
int main() {
bool signal = false;
std::mutex m;
std::condition_variable cv;
std::thread th1(producer, &m, &cv, &signal);
std::thread th2(consumer, &m, &cv, &signal, 1);
std::thread th3(consumer, &m, &cv, &signal, 2);
th1.join();
th2.join();
th3.join();
return 0;
}
Note the enclosing brackets around std::unique_lock<std::mutex> lk(*m);
to provide a local scope.
Upvotes: 1