C++多執行緒中的鎖和條件變數使用
在做多執行緒程式設計時,有兩個場景我們都會遇到:
- 多執行緒訪問共享資源,需要用到鎖;
- 多執行緒間的狀態同步,這個可用的機制很多,條件變數是廣泛使用的一種。
今天我用一個簡單的例子來給大家介紹下鎖和條件變數的使用。
程式碼使用C++11
示例程式碼
#include <iostream> #include <mutex> #include <thread> #include <condition_variable> std::mutexg_mutex;// 用到的全域性鎖 std::condition_variable g_cond;// 用到的條件變數 intg_i= 0; bool g_running = true; void ThreadFunc(int n) {// 執行緒執行函式 for (int i = 0; i < n; ++i) { { std::lock_guard<std::mutex> lock(g_mutex);// 加鎖,離開{}作用域後鎖釋放 ++g_i; std::cout << "plus g_i by func thread " << std::this_thread::get_id() << std::endl; } } std::unique_lock<std::mutex> lock(g_mutex);// 加鎖 while (g_running) { std::cout << "wait for exit" << std::endl; g_cond.wait(lock);// wait呼叫後,會先釋放鎖,之後進入等待狀態;當其它程序呼叫通知啟用後,會再次加鎖 } std::cout << "func thread exit" << std::endl; } int main() { intn = 100; std::thread t1(ThreadFunc, n);// 建立t1執行緒(func thread),t1會執行`ThreadFunc`中的指令 for (int i = 0; i < n; ++i) { { std::lock_guard<std::mutex> lock(g_mutex); ++g_i; std::cout << "plus g_i by main thread " << std::this_thread::get_id() << std::endl; } } { std::lock_guard<std::mutex> lock(g_mutex); g_running = false; g_cond.notify_one();// 通知其它執行緒 } t1.join();// 等待執行緒t1結束 std::cout << "g_i = " << g_i << std::endl; }
程式執行後,關鍵輸出如下:
plus g_i by main thread 139921025066816 plus g_i by main thread 139921025066816 plus g_i by func thread 139921006847744 plus g_i by func thread 139921006847744 plus g_i by func thread 139921006847744 plus g_i by func thread 139921006847744 plus g_i by func thread 139921006847744 wait for exit// func thread等待main thread發來的退出訊號 plus g_i by main thread 139921025066816 plus g_i by main thread 139921025066816 plus g_i by main thread 139921025066816 plus g_i by main thread 139921025066816 plus g_i by main thread 139921025066816 plus g_i by main thread 139921025066816 plus g_i by main thread 139921025066816 plus g_i by main thread 139921025066816 plus g_i by main thread 139921025066816 plus g_i by main thread 139921025066816 func thread exit g_i = 200// 鎖機制保證了g_i的正確
可以看到:
std::this_thread::get_id() g_i
加鎖方法介紹
加鎖相關的程式碼為:
{ std::lock_guard<std::mutex> lock(g_mutex); ...... }
要點為:
-
首先,這在一個區域性作用域內,
std::lock_guard
在構造時,會呼叫g_mutex->lock()
方法; -
區域性作用域程式碼結束後,
std:;lock_guard
的解構函式會被呼叫,函式中會呼叫g_mutex->unlock()
方法。
這樣就實現了加鎖和解鎖的過程,為什麼不直接呼叫加鎖解鎖方法呢?
我想,這是因為如果加鎖和解鎖中間的程式碼出現了問題,導致執行緒函式異常退出,那麼這個鎖就一直無法得到釋放,其它執行緒處理的不好的話,就會造成死鎖了。
條件變數使用介紹
-
當執行緒呼叫
g_cond.wait(lock)
前要先手動呼叫lock->lock()
,這裡是通過std::unique_lock
的構造方法實現的; -
當執行緒呼叫
g_cond.wait(lock)
進入等待後,會呼叫lock->unlock()
方法,所以這也是前面構造lock時使用了std::unique_lock
; -
通知使用的
g_cond.notify_one()
,這個可以通知一個執行緒,另外還有g_cond.notify_all()
用於通知所有執行緒; - 執行緒收到通知的程式碼放在一個while迴圈中,這是為了防止APUE中提到的虛假通知。
結束語
上面是我對C++11中多執行緒加鎖和條件變數使用的基本認識,有不當的地方,還望指正。
參考
cppreference:ofollow,noindex" target="_blank">https://en.cppreference.com/w/cpp/thread