Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

c++11 timed_mutex behaves different from mutex when calling "try_lock_for"

I was expecting that if timed_mutex.try_lock_for could success for all calling threads, like this:

timed_mutex tm;
atomic<int> atTm(0);
void tTMutex(int i) {
    this_thread::sleep_for(chrono::microseconds(200 * i));
    if (tm.try_lock_for(chrono::milliseconds(200 * i))) {
        atTm.fetch_add(1);
    }
}

int main() {
    thread t[3];
    for (size_t i = 0; i < size(t); ++i) {
        t[i] = thread(tTMutex, i);
    }
    ready = true;
    for (size_t i = 0; i < size(t); ++i) {
        t[i].join(); // seems only 1 thread increased atTm
    }
    cout << atTm;
    return 0;
}

It prints "1", while I expected that it could print "3". As long as 3 threads call tm.try_lock_for in different time, I think they should all successfully return, but in fact not: what’s the reason for this?

I changed my program slightly, using ordinary mutex instead of timed_mutex, as below:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

mutex mAllWait; // change 1
atomic<int> atAll(0);
void tMutex() {
    unique_lock<mutex> lk(mAllWait); // change 2
    this_thread::sleep_for(chrono::milliseconds(200));
    atAll.fetch_add(1);
}

int main() {
    thread t[3];
    for (size_t i = 0; i < size(t); ++i) {
        t[i] = thread(tMutex);
    }
    ready = true;
    for (size_t i = 0; i < size(t); ++i) {
        t[i].join(); // all threads can increase atAll
    }
    cout << atAll;
    return 0;
}

It prints 3, as I expected. So what’s the core difference between the 2 programs?

>Solution :

You are not unlocking the mutex after acquiring the lock in the first version:

if (tm.try_lock_for(chrono::milliseconds(200 * i))) {
    atTm.fetch_add(1);
    tm.unlock(); // missing
}

so only the first thread will be able to ever acquire it. That’s why std::unique_lock should be preferred. It unlocks in its destructor. It also accepts std::timed_mutex as template argument and has the try_lock_for member function has well.


That aside:

There is no guarantee that try_lock_for will wait as long as you specified. It may wake up spuriously before the time limit is reached and return false.

That could happen immediately when you start waiting or at any other time before the mutex can be acquired or even if the mutex is currently not acquired by any other thread. Then you don’t increment atTm in that thread.

Of course there is also the possibility that the try_lock_for calls actually do time out and you wont increment for that reason, but I assume as long as you don’t run this on a heavy overloaded system that shouldn’t happen.

Run the try_lock_for call in a loop and check whether the time has actually exceeded if you care about that. This is probably simpler to do with try_lock_until.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading