在前面的三講中我們使用的mutex都是普通的std::mutex,這里介紹一下shared_mutex,版本為C++17
std::shared_mutex
的底層實現時操作系統提供的讀寫鎖,在讀多寫少的情況下,該shared_mutex
比mutex
更加高效。
它提供了常用的四種方法:
lock
和unlock
分別用于獲取寫鎖和解除寫鎖
lock_shared
和unlock_shared
分別用于獲取讀鎖和解除讀鎖
寫鎖模式稱為排他鎖,讀鎖模式稱為共享鎖。
c++11和c++14標準中分別引入unique_lock
和shared_lock
兩個類模板配合shared_mutex
使用。
對象在構造時自動對std::shared_mutex
加鎖,析構時自動對其解鎖。
前者用于加解寫鎖,后者用于加解讀鎖。
當然在第二講中,我們也談到了unique_lock
對于普通鎖mutex的一種應用,當時是和std::lock_guard
對比的。
下面是案例代碼,對于共享資源我們創建多個讀線程和一個寫線程,分別使用std::mutex
和std::shared_mutex
做一下性能測試。
#include <iostream>
#include <thread>
#include <mutex>
#include <shared_mutex>// 讀線程數量
#define READER_THREAD_COUNT 4
// 最大循環次數
#define LOOP_COUNT 500000class shared_mutex_counter {
// 這里使用的鎖是 shared_mutex
private:mutable std::shared_mutex m_mutex;unsigned int m_value = 0; // 多個線程共享的資源
public:shared_mutex_counter() = default;~shared_mutex_counter() = default;// shared_lock 同一時可多個讀線程可以同時訪問m_value的值unsigned int get() const{// 這里使用shared_lockstd::shared_lock<std::shared_mutex> lock(m_mutex);return m_value;}// unique_lock 同一時間僅有一個寫線程可以修改m_value的值void incremet() {std::unique_lock<std::shared_mutex> lock(m_mutex);m_value++;}
};class mutex_counter {
// 這里使用的鎖是 shared_mutex
private:mutable std::mutex m_mutex;unsigned int m_value = 0; // 多個線程共享的資源
public:mutex_counter() = default;~mutex_counter() = default;// 同一時間僅有一個讀線程可以同時訪問m_value的值unsigned int get() const{std::unique_lock<std::mutex> lock(m_mutex);return m_value;}// 同一時間僅有一個寫線程可以修改m_value的值void incremet(){std::unique_lock<std::mutex> lock(m_mutex);m_value++;}
};// 測試 shared_mutex
void test_shared_mutex()
{shared_mutex_counter counter;unsigned int temp;// 寫線程函數auto write = [&counter]() {for (int i = 0; i < LOOP_COUNT; i++) {counter.incremet();}};// 讀線程函數auto read = [&counter, &temp]() {for (int i = 0; i < LOOP_COUNT; i++) {temp = counter.get();}};// 存放讀線程對象指針的數組std::thread** tarray = new std::thread* [READER_THREAD_COUNT];// 記錄起始時間clock_t start = clock();// 創建 READER_THREAD_COUNT 個讀線程for (int i = 0; i < READER_THREAD_COUNT; i++) {tarray[i] = new std::thread(read);}// 創建一個寫線程std::thread tw(write);// 等待joinfor (int i = 0; i < READER_THREAD_COUNT; i++) {tarray[i]->join();}tw.join();clock_t end = clock();std::cout << "test shared_mutex" << std::endl;std::cout << "thread count " << READER_THREAD_COUNT << std::endl;std::cout << "spend time " << end - start << std::endl;std::cout << "result : " << counter.get() << std::endl;std::cout << "temp : " << temp << std::endl;
}// 測試 mutex
void test_mutex()
{mutex_counter counter;unsigned int temp;// 寫線程函數auto write = [&counter]() {for (int i = 0; i < LOOP_COUNT; i++) {counter.incremet();}};// 讀線程函數auto read = [&counter, &temp]() {for (int i = 0; i < LOOP_COUNT; i++) {temp = counter.get();}};// 存放讀線程對象指針的數組std::thread** tarray = new std::thread* [READER_THREAD_COUNT];// 記錄起始時間clock_t start = clock();// 創建 READER_THREAD_COUNT 個讀線程for (int i = 0; i < READER_THREAD_COUNT; i++) {tarray[i] = new std::thread(read);}// 創建一個寫線程std::thread tw(write);// 等待joinfor (int i = 0; i < READER_THREAD_COUNT; i++) {tarray[i]->join();}tw.join();clock_t end = clock();std::cout << "test mutex" << std::endl;std::cout << "thread count " << READER_THREAD_COUNT << std::endl;std::cout << "spend time " << end - start << std::endl;std::cout << "result : " << counter.get() << std::endl;std::cout << "temp : " << temp << std::endl;
}int main() {test_mutex();std::cout << std::endl;test_shared_mutex();return 0;
}
測試結果如下:
test mutex
thread count 8
spend time 1312
result : 500000
temp : 500000test shared_mutex
thread count 8
spend time 601
result : 500000
temp : 475766
有個問題,我同樣程序在windows的clion和vs上跑結果差異較大,clion跑出來的結果shared_mutex耗時比mutex多好多。
linux下的vscode跑出來結果正常。