C++ 11 多執行緒下std::unique_lock與std::lock_guard的區別和用法
阿新 • • 發佈:2018-12-13
這裡主要介紹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 (intid) { 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