日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

C++11使用互斥量保护共享数据

發布時間:2023/12/15 c/c++ 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C++11使用互斥量保护共享数据 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

C++中使用互斥量

在C++11中,可以通過實例化std::mutex創建互斥量,可以通過調用成員函數lock()進行上鎖,調用unlock()進行解鎖。

例如:

int g_num = 0; std::mutex g_num_mutex;void slow_increment(int id) {for (int i = 0; i < 3; ++i){//加鎖g_num_mutex.lock();++g_num;std::cout << id << " => " << g_num << '\n';//若在這期間,發生異常,將導致鎖無法釋放,異常//解鎖g_num_mutex.unlock();std::this_thread::sleep_for(std::chrono::seconds(1));} }int _tmain(int argc, _TCHAR* argv[]) {std::thread td1(slow_increment, 0);std::thread td2(slow_increment, 0);td1.join();td2.join();}

不過,不推薦實踐中直接去調用成員函數,因為調用成員函數就意味著,必須記住在每個函數出口都要去調用unlock(),也包括異常的情況,如果處理不當,將導致鎖無法釋放,程序卡住。

因此,C++庫為互斥量提供了一個RAII語法的模板類std::lock_guard,其會在構造的時候提供已鎖的互斥量,并在析構的時候進行解鎖,從而保證了一個已鎖的互斥量總是會被正確的解鎖。對于以上的代碼,我們只需要將全局變量用lock_guard鎖即可大大提高程序的健壯性。

修改代碼如下:

for (int i = 0; i < 3; ++i) {//使用RAII語法,提高程序的健壯性std::lock_guard<std::mutex> gurad(g_num_mutex);++g_num;... }

基于面向對象準則,保護共享數據
雖然某些情況下,使用全局變量沒問題,但在大多數情況下,互斥量通常會與保護的數據放在同一個類中,而不是定義成全局變量。這是面向對象設計的準則:將互斥量放在一個類中,對類的功能進行封裝,并進行數據保護。在這種情況下,互斥量和要保護的數據都被定義成private成員,這會讓訪問數據的代碼變的清晰。

可靠的代碼設計

進行代碼設計時,一定要特別注意不要返回受保護數據的指針或者引用,否則數據安全是不可以靠的,例如:

template<typename T> class CThreadsafeStack { public://不要設計這樣的接口,是不安全行為//因為用戶可以在類外修改數據std::stack<T>& GetData(){std::lock_guard<std::mutex> guard(m_mutex);return m_data;}private:std::mutex m_mutex; //互斥量std::stack<T> m_data;//受保護的數據 };

接口內在的條件競爭

在C++ STL模板庫中,已經實現stack功能,但是std::stack<>卻不是線程安全的,因為stack成員函數存在惡性條件的數據競爭,導致多線程環境下,數據出現錯誤。

例如:

std::stack<int> st; st.push(100); st.push(200); st.push(300);std::thread td1([&](){int value = st.top(); //其他操作std::this_thread::sleep_for(std::chrono::milliseconds(5));st.pop();//自己實現的打印函數,保證打印互斥,//可以用普通的printf函數實現,只是打印可能不完整safe_print(value); });std::thread td2([&](){int value = st.top();//其他操作std::this_thread::sleep_for(std::chrono::milliseconds(5));st.pop();//自己實現的打印函數,保證打印互斥。safe_print(value); }); td1.join(); td2.join();

?運行結果:

get top value is :300 get top value is :300

由于接口之間沒有鎖機制或者鎖的范圍太小,導致兩個線程對同一個數據進行兩次讀取(都pop之前進行top操作),導致進行數據處理的時候就出現異常情況(一個數據處理兩次)。

從以上實驗以及線程時間片分析,top()和pop()之間存在惡性條件競爭,因為鎖的粒度太小(接口內有鎖或者干脆沒有鎖機制),需要保護的操作并未全覆蓋到導致。

如果需要達到預期效果,需要有這么一個鎖,它能鎖住top和pop兩個操作才可以,這樣鎖的粒度就變大了,從設計上來說,并不合理。因為如果一個系統中鎖的粒度太大,一個線程需要等待較長時間,導致系統的并發性能就受到了限制。因此原有的stack提供的接口在多線程環境中,顯得并不是那么好。

為了達到線程安全以及符合棧的基本操作,需要重新設計線程安全的棧類。
代碼如下:

struct empty_stack : std::exception {const char* what() const throw() {return "empty stack!";}; };template<typename T> class CThreadsafeStack { public:CThreadsafeStack(){}CThreadsafeStack(const CThreadsafeStack& other){std::lock_guard<std::mutex>(other.m_mutex);m_data = other.m_data;} //不支持賦值運算CThreadsafeStack& operator=(const CThreadsafeStack&) = delete;//修改接口以規避接口之間的數據競爭(在pop里面完成top和pop)std::shared_ptr<T> pop(){std::lock_guard<std::mutex> lock(m_mutex);// 在調用pop前,檢查棧是否為空if (m_data.empty()) throw empty_stack(); // 在修改堆棧前,分配出返回值std::shared_ptr<T> const res(std::make_shared<T>(m_data.top())); m_data.pop();return res;}void pop(T& value){std::lock_guard<std::mutex> guard(m_mutex);if (m_data.empty()) throw empty_stack();value = m_data.top();m_data.pop();}void push(T new_value){std::lock_guard<std::mutex> guard(m_mutex);m_data.push(new_value);}bool empty() const{std::lock_guard<std::mutex> guard(m_mutex);return m_data.empty();}std::size_t size() const//const類型{std::lock_guard<std::mutex> guard(m_mutex);return m_data.size();}private:mutable std::mutex m_mutex; //lock_guard傳遞的是引用類型std::stack<T> m_data; };

測試代碼:

CThreadsafeStack <int> st; //僅一個數據 st.push(1); std::function <void (int)> func = [&](int timeout) {try{if (!st.empty()){std::this_thread::sleep_for(std::chrono::milliseconds(timeout));int nValue = 0;st.pop(nValue);safe_print(nValue);}}catch (empty_stack &e){std::cout << e.what() << std::endl;} };std::thread td1(func,2); std::thread td2(func,3);td1.join(); td2.join();

運行結果:

//彈出一個數據,另外一個報異常 符合預期 get value is :1 empty stack!.

原文鏈接:https://blog.csdn.net/c_base_jin/article/details/89440786

總結

以上是生活随笔為你收集整理的C++11使用互斥量保护共享数据的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。