Mutex(mutual exclusion): It provides the concurrent access to resource.
In c++ 11, We will not use it (mutex) directly (I mean we can use but we have better option of RAII principal (If exception happens after acquiring the mutex then it will be released in destruction otherwise it would have locked forever)), it will be used through two classes:
- std::lock_guard
- std::unique_lock
Difference between unique lock and lock guard (unique lock vs lock guard) : TODO
std:lock_guard: It is scoped lock, It will get the mutex in constructor and release the mutex after completion of scope(block) in destructor. It has only three API:
- lock_guard obj1(m) //create object to lock the mutex.
- lock_guard obj2(m, adopt_lock) //create object to lock the already locked mutex.
- ~lock_guard() //release the mutex.
std::unique_lock: It is the superior version of lock guard, It has same API of lock_guard and additional API like lock, unlock and try lock etc. so that we can control the mutex from externally.
- unique_lock obj1(m) //create object to lock the mutex as like above
- unique_lock obj2(m, adopt_lock) //create object to lock the already locked mutex
- ~unique_lock() //release the mutex
- obj1.lock() //Additional API to lock the mutex, it blocking call
- obj1.unlock() //Additional API to unlock the mutex
- try_lock() //Additional API to lock the mutex and return immediately with either true or false
- try_lock_for() //Additional API to lock the mutex and return after given duration with either true or false.
Source code of lock guard with example:
/* Program: Mutex with Lock Guard Author: Alpha Master Date: 7 Feb 2021 */ //Header File #include<iostream> #include<thread> #include<chrono> #include<mutex> //Global Variable std::mutex gMtx; //Print Thread void PrintThread(std::string str) { { std::lock_guard<std::mutex> lg(gMtx); //purposefully waiting to verify the result std::this_thread::sleep_for(std::chrono::seconds(1)); std::cout<<"Value: "; for(auto i : str) { std::cout<<i; } std::cout<<std::endl; } } int main() { std::cout<<"Mutex with Lock Guard"<<std::endl; //Thread 1 and its joinable std::thread t1(PrintThread, "please print this"); //Thread 2 and its also joinable std::thread t2(PrintThread, "This should be printed"); //std::cout<<"Main thread is Waiting for Child thread"<<std::endl; t1.join(); t2.join(); return 0; };
Output WITHOUT mutex:
Mutex with Lock Guard Value: Value: Tphliease psr isnt thihsould b e printed
Output with mutex:
Mutex with Lock Guard Value: please print this Value: This should be printed
Source code of unique lock with example:
/* Program: Mutex with Unique Lock Author: Alpha Master Date: 7 Feb 2021 */ //Header File #include<iostream> #include<thread> #include<chrono> #include<mutex> //Global Variable std::mutex gMtx; //Print Thread void PrintThread(std::string str) { { std::unique_lock<std::mutex> lg(gMtx); //purposefully waiting to verify the result std::this_thread::sleep_for(std::chrono::seconds(1)); std::cout<<"Value: "; for(auto i : str) { std::cout<<i; } std::cout<<std::endl; if(lg) { lg.unlock(); std::cout<<"Mutex status: "<<lg.owns_lock()<<std::endl; //lock the mutex again, will release in destructor lg.lock(); std::cout<<"Mutex status: "<<lg.owns_lock()<<std::endl; } } } int main() { std::cout<<"Mutex with Lock Guard"<<std::endl; //Thread 1 and its joinable std::thread t1(PrintThread, "please print this"); //Thread 2 and its also joinable std::thread t2(PrintThread, "This should be printed"); //std::cout<<"Main thread is Waiting for Child thread"<<std::endl; t1.join(); t2.join(); return 0; };
Output:
Mutex with Lock Guard Value: This should be printed Mutex status: 0 Mutex status: 1 Value: please print this Mutex status: 0 Mutex status: 1