简体   繁体   中英

Relationship of std::unique_lock<mutex> and conditional_variable cond

Here is my code:

Class

class carl{
public:
int x = 0;
std::mutex _mu;
std::condition_variable cond;
bool donecreating = false;

void createFood(){


    if(x == 0){
    std::unique_lock<mutex> locker(_mu);
    x++;
    std::cout<<"creating food.."<<std::endl;
    cout<<"Food count: "<<x<<endl;
    locker.unlock();

    cond.notify_one();                                 //notify
    std::this_thread::sleep_for(chrono::seconds(1));   //sleep
    }

}   

void eatFood(){

    std::unique_lock<mutex> locker(_mu);                //lock
    std::cout<<"i am executing"<<std::endl;             //notif
    cond.wait(locker);                                  //wait


    x--;                                                //process food
    std::cout<<"eating food.."<<std::endl;  
    cout<<"Food left: "<<x<<endl;
    locker.unlock();

}   
};

Function aka thread one

void create(carl& carl){
for(int i=0;i>-100;i--){        //create 100 times

  carl.createFood();

}   

carl.donecreating  = true;      //done creating 100 food 
}

Main

int main(int argc, char** argv) {
carl c;                                    //init object
std::thread t1(create,std::ref(c));        //init thread


while(c.donecreating != true){            //exit condition is if the class is done creating food 100 times

c.eatFood();                            

}

t1.join();

return 0;
}

Output:

i am executing
creating food...
Food count: 1
eating food..
Food left: 0

I am trying to trace my code and here is my understanding so far and i need some clarifications

1.) Upon compile, main thread(consumer) is faster than the producer thread so it is fired up first and was put to sleep by cond.wait(locker); to prevent it from eating because no food was made yet. but before cond.wait(locker); there is std::unique_lock<mutex> locker(_mu); , is it automatically unlocked so that the other thread can access it while waiting?

2.) If ever the createFood was fired up first(because threads are processor based? it's possible right?), it will send the cond.notify_one(); if there is one and if not then it will just simply go on creating a food and then sleep . The other thread will start processing because the mutex will be unlocked then it will reach cond.wait(locker); but there is a food already so sleep won't be necessary the solution that i found out is by implementing spurious wake , is that what it is for?

3.) I'm still really curious about std::unique_lock<mutex> locker(_mu); like what really happens if the other thread reached that line of code and it's currently locked? does it ignore every line below that and just move on until that block of code gets out of scope? or does it stops on that line and wait until it gets unlocked?

To start with, your code has a data race (and hence undefined behavior) since if(x == 0) in createFood() accesses x unprotected. Every access to x , both reads and writes, must be protected by the mutex.

is it automatically unlocked so that the other thread can access it while waiting?

Yes, wait() unlocks the mutex before blocking on the condition variable, and then locks the mutex again before it returns.

The other thread will start processing because the mutex will be unlocked then it will reach cond.wait(locker); but there is a food already so sleep won't be necessary the solution that i found out is by implementing spurious wake, is that what it is for?

No, spurious wakeup is not something you have control over. Your code has two problems:

  • it can miss the notification, as you observe.
  • The wait in eatFood() can end spuriously, in which case there isn't actually any food to eat, but you don't handle this case either.

The solution is to run wait in a loop, until there is food available. This is equivalent to running the version of wait taking a predicate:

while(x == 0) cond.wait(locker); 

or

cond.wait(locker, [this](){ return x > 0; });

I'm still really curious about std::unique_lock<mutex> locker(_mu); like what really happens if the other thread reached that line of code and it's currently locked? does it ignore every line below that and just move on until that block of code gets out of scope? or does it stops on that line and wait until it gets unlocked?

The latter. After that line, the mutex is locked by the thread constructing locker . This means blocking and waiting if necessary.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM