多線程同步
- 引言
- 總述
- 詳情
- 互斥鎖
- 示例
- 運行結果
- 分析
- 條件變量
- 示例一
- 實現
- 分析
- 優化
- 運行結果
- 示例二
- 實現代碼
- 運行結果
- 示例三
- 實現代碼
- 運行結果
- 讀寫鎖
- 示例
- 實現代碼
- 注意
- 分析
- 運行結果
- 附言
- 實現
- 運行結果
- 運行結果
- 個人心得
引言
項目中使用多線程,會遇到兩種問題,一種是對共享資源的訪問時需要考慮多線程競爭訪問導致的不是預期的結果,另一種是多線程之間需要同步的問題。其實質歸根結底就是多線程之間的同步。
本文主要在C++11的基礎之上,結合示例講述多線程同步的幾種方法。
本文為上篇。
總述
C++中多線程同步的方式分為:
互斥鎖,條件變量,讀寫鎖,信號量,future和promise,原子操作,線程局部存儲。
詳情
下面根據上面提到的七種線程同步的方式分別給出示例。
互斥鎖
互斥鎖用于解決多線程間對共享資源的競爭問題,具有排它性。一旦一個線程獲取鎖,并加鎖,其他的線程只能阻塞等待該線程解鎖之后再搶占鎖,且每次只能有一個線程獲得鎖,沒有獲得鎖的線程只能阻塞等待。
示例
下面是互斥鎖的示例:
#include <iostream>
#include <thread>
#include <mutex>using namespace std;
mutex g_mutex;void fun(int n,const char& c) {g_mutex.lock();cout << "子線程的線程id:" <<this_thread::get_id()<<"開始執行該線程......"<< endl;for (int i = 0; i < n;++i) {cout << c;}cout << endl;cout << this_thread::get_id()<<"子線程結束" << endl;g_mutex.unlock();
}int main()
{thread t1(fun,5,'S');thread t2(fun,6,'*');t1.join();t2.join();cout << "主線程的id:" << this_thread::get_id() << endl;return 0;
}
運行結果
分析
上面的示例創建了兩個子線程,執行相同的線程處理函數,這就涉及到多線程對共享資源的競爭問題,這里兩個子線程都搶著調用線程處理函數fun。由于何時加鎖,在哪里加鎖,需要結合開發人員的實際需求而定。這個示例希望程序能夠輸出完整的一個子線程調用fun函數后的內容,所以在剛進入線程處理函數和離開線程處理函數的時候進行加鎖和解鎖。
若是希望只給fun函數中的循環打印部分加鎖,可以這樣修改(只修改線程處理函數fun加鎖,解鎖位置,其它不變):
void fun(int n,const char& c) {cout << "子線程的線程id:" <<this_thread::get_id()<<"開始執行該線程......"<< endl;g_mutex.lock();for (int i = 0; i < n;++i) {cout << c;}g_mutex.unlock();cout << endl;cout << this_thread::get_id()<<"子線程結束" << endl;
}
執行的結果:
可以看到上面的示例,變動了加鎖和解鎖的位置之后,很明顯的出現了資源競爭,輸出后結果出現了混亂。當然輸出結果也會出現很多種,無法確定。像下面這樣,是再次運行被修改加鎖和解鎖的位置之后的運行結果。
也可能是這樣的運行結果:
對于加鎖的部分,當前獲取鎖的子線程可以保證其連續執行,但是不加鎖的部分就會出現資源競爭搶占,最終兩個子線程的同一個線程處理函數fun中不加鎖的內容會穿插著輸出,達不到想要的效果。
條件變量
條件變量需要與互斥鎖搭配使用來達到想要的效果。
示例一
實現
下面是實現代碼:
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>using namespace std;
mutex g_mutex;
condition_variable g_cond;
bool IsReady = false;
const int g_num = 10;void fun(int n) {unique_lock<mutex> lock(g_mutex);while (!IsReady) {cout << "線程被阻塞....." << endl;g_cond.wait(lock);}cout << "線程" <<this_thread::get_id()<<"執行完成!"<< endl;
}void wakeUp() {this_thread::sleep_for(chrono::milliseconds(2));//延遲2毫秒,為了讓子線程出現阻塞等待的過程unique_lock<mutex> lock(g_mutex);IsReady = true;cout