1. 程式人生 > >C++ 11 多執行緒下std::unique_lock與std::lock_guard的區別和用法

C++ 11 多執行緒下std::unique_lock與std::lock_guard的區別和用法

這裡主要介紹std::unique_lock與std::lock_guard的區別用法

先說簡單的

一、std::lock_guard的用法

std::lock_guard其實就是簡單的RAII封裝,在建構函式中進行加鎖,解構函式中進行解鎖,這樣可以保證函式退出時,鎖一定被釋放。

簡單來說,就是防止開發者粗心大意,函式在分支中return時,忘記unlock操作導致後續操作全部被掛起甚至引發死鎖情況的。

用法如下:

// lock_guard example
#include <iostream>       // std::cout
#include <thread>         //
std::thread #include <mutex> // std::mutex, std::lock_guard #include <stdexcept> // std::logic_error std::mutex mtx; void print_even (int x) { if (x%2==0) std::cout << x << " is even\n"; else throw (std::logic_error("not even")); } void print_thread_id (int
id) { try { // using a local lock_guard to lock mtx guarantees unlocking on destruction / exception: std::lock_guard<std::mutex> lck (mtx); print_even(id); } catch (std::logic_error&) { std::cout << "[exception caught]\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; }

 

二、std::unique_lock的用法

std::unique_lock的功能相比std::lock_guard來說,就強大多了,是std::lock_guard的功能超集, 封裝了各種加鎖操作,阻塞的,非阻塞的,還可以結合條件變數一起使用,基本上對鎖的各種操作都封裝了,當然了,功能豐富是有代價的,那就是效能和記憶體開銷都比std::lock_guard大得多,所以,需要有選擇地使用。

std::unique_lock也會在析構的時候自動解鎖,所以說,是std::lock_guard的功能超集。

看看std::unique_lock的建構函式,支援三種加鎖模式:

unique_lock( mutex_type& m, std::defer_lock_t t );   //延遲加鎖
unique_lock( mutex_type& m, std::try_to_lock_t t ); //嘗試加鎖
unique_lock( mutex_type& m, std::adopt_lock_t t );   //馬上加鎖

 

幾個主要操作函式:

lock()         //阻塞等待加鎖
try_lock()        // 非阻塞等待加鎖
try_lock_for()  //在一段時間內嘗試加鎖
try_lock_until()   //在某個時間點之前嘗試加鎖

 

接下來,給個例子:

#include <mutex>
#include <thread>
#include <iostream>
#include <vector>
#include <chrono>
 
int main()
{
    int counter = 0;
    std::mutex counter_mutex;
    std::vector<std::thread> threads;
 
    auto worker_task = [&](int id) {
        std::unique_lock<std::mutex> lock(counter_mutex);
        ++counter;
        std::cout << id << ", initial counter: " << counter << '\n';
        lock.unlock();
 
        // don't hold the lock while we simulate an expensive operation
        std::this_thread::sleep_for(std::chrono::seconds(1));
 
        lock.lock();
        ++counter;
        std::cout << id << ", final counter: " << counter << '\n';
    };
 
    for (int i = 0; i < 10; ++i) threads.emplace_back(worker_task, i);
 
    for (auto &thread : threads) thread.join();
}

Output:

0, initial counter: 1
1, initial counter: 2
2, initial counter: 3
3, initial counter: 4
4, initial counter: 5
5, initial counter: 6
6, initial counter: 7
7, initial counter: 8
8, initial counter: 9
9, initial counter: 10
6, final counter: 11
3, final counter: 12
4, final counter: 13
2, final counter: 14
5, final counter: 15
0, final counter: 16
1, final counter: 17
7, final counter: 18
9, final counter: 19
8, final counter: 20