[c++11]多線程編程(五)——unique_lock

互斥鎖保證了線程間的同步,可是卻將並行操做變成了串行操做,這對性能有很大的影響,因此咱們要儘量的減少鎖定的區域,也就是使用細粒度鎖編程

這一點lock_guard作的很差,不夠靈活,lock_guard只能保證在析構的時候執行解鎖操做,lock_guard自己並無提供加鎖和解鎖的接口,可是有些時候會有這種需求。看下面的例子。併發

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;
        }
    }

};

上面的代碼中,一個函數內部有兩段代碼須要進行保護,這個時候使用lock_guard就須要建立兩個局部對象來管理同一個互斥鎖(其實也能夠只建立一個,可是鎖的力度太大,效率不行),修改方法是使用unique_lock。它提供了lock()unlock()接口,能記錄如今處於上鎖仍是沒上鎖狀態,在析構的時候,會根據當前狀態來決定是否要進行解鎖(lock_guard就必定會解鎖)。上面的代碼修改以下:函數

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(); //臨時解鎖

        //do something 2

        guard.lock(); //繼續上鎖
        // do something 3
        f << msg << id << endl;
        cout << msg << id << endl;
        // 結束時析構guard會臨時解鎖
        // 這句話可要可不要,不寫,析構的時候也會自動執行
        // guard.ulock();
    }

};

上面的代碼能夠看到,在無需加鎖的操做時,能夠先臨時釋放鎖,而後須要繼續保護的時候,能夠繼續上鎖,這樣就無需重複的實例化lock_guard對象,還能減小鎖的區域。一樣,可使用std::defer_lock設置初始化的時候不進行默認的上鎖操做:性能

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(); //臨時解鎖

    //do something 2

    guard.lock(); //繼續上鎖
    // do something 3
    f << msg << id << endl;
    cout << msg << id << endl;
    // 結束時析構guard會臨時解鎖
}

這樣使用起來就比lock_guard更加靈活!而後這也是有代價的,由於它內部須要維護鎖的狀態,因此效率要比lock_guard低一點,在lock_guard能解決問題的時候,就是用lock_guard,反之,使用unique_lock學習

後面在學習條件變量的時候,還會有unique_lock的用武之地。線程

另外,請注意,unique_locklock_guard都不能複製,lock_guard不能移動,可是unique_lock能夠!code

// unique_lock 能夠移動,不能複製
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 不能移動,不能複製
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

參考

  1. C++併發編程實戰
  2. C++ Threading #5: Unique Lock and Lazy Initialization
相關文章
相關標籤/搜索