c++11 Multithreaded Programming (V)--------- unique_lock

Mutex locks ensure synchronization between threads, but they turn parallel operations into serial operations, which have a significant impact on performance, so we want to minimize the area of locks that we lock, that is, use fine-grained locks.

This lock_guard is not good, not flexible enough, lock_guard can only guarantee unlocking at the time of destruction, lock_guard itself does not provide an interface for locking and unlocking, but there are times when this is required. See the example below.

class LogFile {
    std::mutex _mu;
    ofstream f;
public:
    LogFile() {
        f.open("log.txt");
    }
    ~LogFile() {
        f.close();
    }
    void shared_print(string msg, int id) {
        {
            std::lock_guard<std::mutex> guard(_mu);
            //do something 1
        }
        //do something 2
        {
            std::lock_guard<std::mutex> guard(_mu);
            // do something 3
            f << msg << id << endl;
            cout << msg << id << endl;
        }
    }

};

In the above code, there are two pieces of code inside a function that need to be protected, using lock_ Guard needs to create two local objects to manage the same mutex (in fact, only one can be created, but the locks are too powerful and inefficient). The modification is to use unique_lock. It provides lock() and unlock() interfaces that record whether a lock is currently or not locked and, at the time of destruction, decides whether to unlock based on the current state (lock_guard is always unlocked). The above code is modified as follows:

class LogFile {
    std::mutex _mu;
    ofstream f;
public:
    LogFile() {
        f.open("log.txt");
    }
    ~LogFile() {
        f.close();
    }
    void shared_print(string msg, int id) {

        std::unique_lock<std::mutex> guard(_mu);
        //do something 1
        guard.unlock(); //Temporary unlock

        //do something 2

        guard.lock(); //Continue locking
        // do something 3
        f << msg << id << endl;
        cout << msg << id << endl;
        // Destructing guard at the end temporarily unlocks
        // Do not write this sentence, it will be executed automatically when it is destructed
        // guard.ulock();
    }

};

As you can see from the code above, you can temporarily release the lock when there is no need to lock it, then continue to lock if you need to continue protecting it, so you don't need to instantiate lock_repeatedly Guard objects also reduce the area of locks. Similarly, std::defer_can be used The lock settings are initialized without the default lock operation:

void shared_print(string msg, int id) {
    std::unique_lock<std::mutex> guard(_mu, std::defer_lock);
    //do something 1

    guard.lock();
    // do something protected
    guard.unlock(); //Temporary unlock

    //do something 2

    guard.lock(); //Continue locking
    // do something 3
    f << msg << id << endl;
    cout << msg << id << endl;
    // Destructing guard at the end temporarily unlocks
}

This makes it easier to use than lock_guard is more flexible! Then there's a cost, too, because it's more efficient than lock_because it maintains the state of the lock internally Guard lower, at lock_ When guard solves a problem, it uses lock_guard, otherwise, use unique_lock.

Later, when you learn the conditional variable, there will be unique_ Where lock is used.

Also, notice that unique_lock and lock_ Guards cannot be copied, lock_guard cannot move, but unique_lock is OK!

// unique_lock can be moved, not copied
std::unique_lock<std::mutex> guard1(_mu);
std::unique_lock<std::mutex> guard2 = guard1;  // error
std::unique_lock<std::mutex> guard2 = std::move(guard1); // ok

// lock_guard cannot be moved, cannot be copied
std::lock_guard<std::mutex> guard1(_mu);
std::lock_guard<std::mutex> guard2 = guard1;  // error
std::lock_guard<std::mutex> guard2 = std::move(guard1); // error

Reference resources

  1. C++ Concurrent Programming Practice
  2. C++ Threading #5: Unique Lock and Lazy Initialization

Keywords: C++

Added by alex_funky_dj on Thu, 02 Dec 2021 03:24:29 +0200