Reputation: 1023
Given n, below code aims to print "foo", "bar" alternatively.
For example,
Input: n = 2
Output: "foobarfoobar"
Explanation: "foobar" is being output 2 times.
detailed code is listed here. There are two questions:
1) Which one is better, declaring mutex lock outside or inside for-loop?
2) When we declare mutex lock outside the for-loop, why we can NOT call lock.unlock()
before cv.notify_one();
. It produces runtime error!
class FooBar {
private:
mutex m;
condition_variable cv;
int n;
bool flag = false; // for foo printed
public:
FooBar(int n) {
this->n = n;
}
void foo(function<void()> printFoo) {
//question: can we put here? unique_lock<mutex> lock(m);
for (int i = 0; i < n; i++) {
unique_lock<mutex> lock(m); //question: why shall this lock be declared again and again in each for-loop?
cv.wait(lock, [&](){ return !flag; });
// printFoo() outputs "foo". Do not change or remove this line.
printFoo();
flag = true;
//question: can we call lock.unlock()? yes, it works b/c each for-loop iteration grabs a new lock! if we declare lock outside for-loop, then we can NOT unlock here? why?
lock.unlock();
cv.notify_one();
}
}
void bar(function<void()> printBar) {
for (int i = 0; i < n; i++) {
unique_lock<mutex> lock(m);
cv.wait(lock, [&](){ return flag; }); //wait until lambda returns true
// printBar() outputs "bar". Do not change or remove this line.
printBar();
flag = false;
lock.unlock();
cv.notify_one();
}
}
};```
Upvotes: 2
Views: 2348
Reputation: 20918
It doesn't matter where you define unique_lock
(inside or outside loop).
All what you need to do is to ensure that mutex
is locked when condition_variable::wait
is called.
You got runtime error, because when you define unique_lock
outside loop
unique_lock<mutex> lock(m); // mutex is LOCKED
for (int ...) {
cv.wait(); // problem is here at second iteratio
lock.unlock();
cv.notify_one();
}
mutex is locked only in first iteration of for (it was locked in ctor of unique_lock
).
In second iteration wait
is called with unlocked lock, since c++14 it leads std::terminate
to be called (
you can read about this behaviour here).
unique_lock
has overloaded constructor taking std::defer_lock_t
type tag.
When this ctor is called passed mutex is not being locked. Then when you want to "open" critical section
on given mutex you need to call explicitly lock
function.
So, the two versions below do the same thing:
1)
unique_lock<mutex> lock(m, std::defer_lock); // mutex is un-locked
for ()
{
lock.lock();
cv.wait(...); // LOCK is locked
lock.unlock();
cv.notify_one();
}
2)
for ()
{
unique_lock<mutex> lock(m); // is locked
cv.wait();
lock.unlock();
cv.notify_one();
}
Upvotes: 2