thinkdeep
thinkdeep

Reputation: 1023

C++ mutex lock declared within or outside for-loop

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

Answers (1)

rafix07
rafix07

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

Related Questions