C++11 Multithreading - Mutex Lock

Race Condition: A situation like this, where several processes access and manipulate the same data concurrently and the outcome of the execution depends on the particular order in which the access takes place, is called a race condition.

Mutex locks (or spinlocks) are generally considered the simplest of synchronization tools.

The main disadvantage of the implementation given here is that it requires busy waiting.

In the C++11 threading library, the mutexes are in the <mutex> header file. The class representing a mutex is the std::mutex class.

There are two important methods of mutex: lock() and unlock()

// mutex::lock/unlock
#include <iostream>       // std::cout
#include <thread>         // std::thread
#include <mutex>          // std::mutex

std::mutex mtx;           // mutex for critical section

void print_thread_id (int id) {
    // critical section (exclusive access to std::cout signaled by locking mtx):
    mtx.lock();
    std::cout << "thread #" << id << '\n';
    mtx.unlock();
}

int main (){
    std::thread threads[10];
    // spawn 10 threads:
    for (int i=0; i<10; ++i)
        threads[i] = std::thread(print_thread_id,i+1);
    for (auto& th : threads) th.join();
    return 0;
}

std::lock_guard

std::lock_guard is a class template, which implements the RAII for mutex.
It wraps the mutex inside it’s object and locks the attached mutex in its constructor. When it’s destructor is called it releases the mutex.

// mutex::lock/unlock
#include <iostream>       // std::cout
#include <thread>         // std::thread
#include <mutex>          // std::mutex

std::mutex mtx;           // mutex for critical section

void print_thread_id (int id) {
    // critical section (exclusive access to std::cout signaled by locking mtx):
    std::lock_guard<std::mutex> lockGuard(mtx);
    
    std::cout << "thread #" << id << '\n';
    
    // Once function exits, then destructor of lockGuard Object will be called.
    // In destructor it unlocks the mutex.
}

int main (){
    std::thread threads[10];
    // spawn 10 threads:
    for (int i=0; i<10; ++i)
        threads[i] = std::thread(print_thread_id,i+1);
    for (auto& th : threads) th.join();
    return 0;
}

std::unique_lock

lock_guard and unique_lock are pretty much the same thing; lock_guard is a restricted version with a limited interface.

lock_guard always holds a lock from its construction to its destruction. A unique_lock can be created without immediately locking, can unlock at any point in its existence, and can transfer ownership of the lock from one instance to another.

扫描二维码关注公众号,回复: 7461454 查看本文章

So you always use lock_guard, unless you need the capabilities of unique_lock

condition_variable needs a unique_lock.

// mutex::lock/unlock
#include <iostream>       // std::cout
#include <thread>         // std::thread
#include <mutex>          // std::mutex

std::mutex mtx;           // mutex for critical section

void print_thread_id (int id) {
    // critical section (exclusive access to std::cout signaled by locking mtx):
    std::unique_lock<std::mutex> lock(mtx);
    std::cout << "thread #" << id << '\n';
}

int main (){
    std::thread threads[10];
    // spawn 10 threads:
    for (int i=0; i<10; ++i)
        threads[i] = std::thread(print_thread_id,i+1);
    for (auto& th : threads) th.join();
    return 0;
}

Reference

http://www.cplusplus.com/reference/mutex/mutex/lock/

https://thispointer.com//c11-multithreading-part-4-data-sharing-and-race-conditions/

https://thispointer.com//c11-multithreading-part-5-using-mutex-to-fix-race-conditions/

https://stackoverflow.com/questions/20516773/stdunique-lockstdmutex-or-stdlock-guardstdmutex

猜你喜欢

转载自www.cnblogs.com/hankunyan/p/11669375.html