-1

I am learning C++ threads and i don't understand unique_lock mechanism very well. I reed This Link with Conditional variable, and more examples here but still I have my confusions:

1- So my question clearly is, doesn't unique_lock protect the mutual exclusion? I see in some examples when we use it on a shared mutex, the second thread cannot enter to that area which what I expect. But in this example as you see the output, all the threads can pass this line: std::unique_lockstd::mutex lck(mtx); is it just declaration or mutex gets locked as it declared?

2- why does the .lock() cause abort error? If I comment out that line all the threads starts in a row as you see in the screen shot output. I expect only thread0 pass the std::unique_lock<std::mutex> lck(mtx); it should be locked for other threads

enter image description here

Thanks

#include <mutex>
using namespace std;

condition_variable cv;
bool ready = false;
mutex mtx;
void print_id(int id) {

    // why all the threads can pass this line?
    std::unique_lock<std::mutex> lck(mtx);
    
   //i knew about the concept of two times locking, just thought there 
   //is something wrong with the constructor or i dont understand
    lck.lock(); // Having this line gives me abort.

    std::cout << "thread Starts: " << id << '\n';
    while (!ready) 
        cv.wait(lck);
    // ...
    std::cout << "thread Ends: " << id << '\n';
}

void go() {
    std::unique_lock<std::mutex> lck(mtx);
    ready = true;
    cv.notify_all();
}

void main()
{
    std::thread threads[5];
    // spawn 10 threads:
    for (int i = 0; i < 5; ++i)
    {
        this_thread::sleep_for(chrono::milliseconds(2000));
        threads[i] = std::thread(print_id, i);
    }

    std::cout << "10 threads ready to race...\n";
    go();                       // go!

    for (auto& th : threads) th.join();
}
Azzurro94
  • 479
  • 1
  • 7
  • 19
  • 8
    The `unique_lock` constructor you are using [already locks the mutex](https://en.cppreference.com/w/cpp/thread/unique_lock/unique_lock). The call to `lck.lock()` tries to lock a second time, which is illegal. (You were already relying on the "lock at construction" behavior in the `go` function, so it's not clear why you thought that one function needed an explicit lock and the other didn't.) – Raymond Chen Jan 18 '23 at 17:05
  • you are right , my confusion was why all of the threads can pass the first line? if the mutex is locked by first thread? thx – Azzurro94 Jan 18 '23 at 17:20

1 Answers1

2

std::unique_lock is an RAII type. When an object of that type is constructed, it locks the mutex that was passed to it, and upon destruction it unlocks the mutex, so you have scope level locking and unlocking.

All that means is that when you do lck.lock(); you are trying to lock a mutex you have already locked by creating lck. std::unique_lock::lock() will throw an exception when you do this, and it is that uncaught exception that is causing abort() to be called.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
NathanOliver
  • 171,901
  • 28
  • 288
  • 402
  • I see your point. my first question was why all of the threads can pass the first line? i tried the second lock() because i though there is something wrong in the constructor. thx – Azzurro94 Jan 18 '23 at 17:21
  • 3
    @Azzurro94 Ah, `wait` unlocks the passed in mutex and blocks the thread that called `wait` from continuing. If it didn't you'd never be able to get anything done. – NathanOliver Jan 18 '23 at 17:30
  • got it , so the wait on conditional will unblock the mutex, thread2 can enter the area and lock the mutex for second time. meanwhile the firs thread is waited on conditional_variable. Thanks – Azzurro94 Jan 18 '23 at 17:33