C++ scoped_lock,unique_lock,lock_guard


考慮互斥量的使用,最基本的代碼是:

 1 #include <iostream>
 2 #include <thread>
 3 #include <mutex>  
 4 
 5 std::mutex g_my_mutex;
 6 
 7 std::lock_guard<std::mutex> make_lock() {
 8     return std::lock_guard<std::mutex>(g_my_mutex);
 9 }
10 
11 void workOnResource1() {
12 
13     for (int i = 0; i < 10000; ++i)
14     {
15         std::lock_guard<std::mutex> lk(g_my_mutex);
16         --gi;
17     }
18 }
19 
20 void workOnResource2() {
21 
22     for (int i = 0; i < 10000; ++i)
23     {
24         auto lk = make_lock();
25         ++gi;
26     }
27 }
28 
29 int main() {
30 
31     std::thread t1(workOnResource1);
32     std::thread t2(workOnResource2);
33 
34     t1.join();
35     t2.join();
36     
37     std::cout << "gi=" <<gi;
38 }

這在很多例子里都常見。等等,為什么第8行編譯不過去?那是因為您沒有在C++17下編譯。std::lock_guard是禁止拷貝和移動的。C++17 granteed copy ellision允許第8行編譯通過。

比較一下,第24行和第15行,哪個更簡潔一些呢?

std::lock_guard還可以這樣用:

void workOnResource1() {

    for (int i = 0; i < 10000; ++i)
    {
        g_my_mutex.lock();
        std::lock_guard<std::mutex> lk(g_my_mutex, std::adopt_lock);
        --gi;
    }
}

意思就是,當lk對象構造時,不去調用mutex::lock()。因為之前自己所持有的鎖已經lock了一次了(g_my_mutex.lock())。adopt就是這個意思的表達,英文的含義是收養。

當lk析構時,對g_my_mutex調用mutex::unlock,這一點沒有變化。

現在考慮unique_lock的用法:用於更復雜的互斥量操作上,例如:有超時時間的加鎖。

#include <iostream>
#include <thread>
#include <mutex>          // std::mutex, std::lock_guard 
#include <chrono>

using Ms = std::chrono::milliseconds;
using namespace std;

int gi = 0;

std::timed_mutex g_my_mutex;

std::lock_guard<std::timed_mutex> make_lock() {
    return std::lock_guard<std::timed_mutex>(g_my_mutex);  //絕不能分行寫
}

void workOnResource1() {

    for (int i = 0; i < 10000; ++i)
    {
        auto lk = make_lock();
        ++gi;
    }
}

std::unique_lock<std::timed_mutex> make_lock2() {
    std::unique_lock<std::timed_mutex> lk(g_my_mutex, std::defer_lock);
    return lk;  //故意分行寫
}

void workOnResource2() {

    for (int i = 0; i < 10000; ++i)
    {
        auto lk = make_lock2();
        while(lk.try_lock_for(Ms(100))==false){
            std::cout << "lock fail. reason timeout. now retry...";
        }
        --gi;
    }
}

int main() {

    std::thread t1(workOnResource1);
    std::thread t2(workOnResource2);

    t1.join();
    t2.join();
    
    std::cout << "gi=" <<gi;
}

unique_lock支持move語義,這樣它就能飛出{}之外了。像極了std::unique_ptr。同樣的,它也是RAII的,當析構時,調用mutex::unlock().

std::defer_lock是個全局變量,類型是std::defer_lock_t。顯然,它用於函數的重載解析時,選不同的函數執行的。defer_lock的意思是,暫時不對g_my_mutex調用任何加鎖動作。

為了證明,unique_lock是可以move的,可以這樣:

std::unique_lock<std::timed_mutex> lk(g_my_mutex, std::defer_lock);
std::unique_lock<std::timed_mutex> lk2(std::move(lk));
lk2.lock();

scoped_lock是C++17新引進的,在處理多個互斥量時,特別簡單:

參考:http://en.cppreference.com/w/cpp/thread/scoped_lock


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM