並發編程(3)線程間共享數據
阿新 • • 發佈:2018-06-03
中間 template 模板 共享 簡單 PE pub roc 返回
據的指針或引用,2)成員函數沒有通過指針或引用的方式來調用外部沒有被保護的函數或者變量,數據就是安全的。
一、共享內存帶來的問題
讀時沒問題,寫時會有競爭問題。
二、解決方法
1、最簡單的辦法就是對數據結構采用某種保護機制,確保只有進行修改的線程才能看到不變量被破壞時的中間狀態。從其他訪問線程的角度來看,修改不是已經完成了,就是還沒開始。
2、另一個選擇是對數據結構和不變量的設計進行修改,修改完的結構必須能完成一系列不可分
割的變化,也就是保證每個不變量保持穩定的狀態,這就是所謂的無鎖編程。另一種處理條件競爭的方式是,使用事務的方式去處理數據結構的更新。(STM)
第二種是比較高階的內容,不在討論範圍內。討論第一種也就是使用互斥量保護數據。
三、std::mutex 創建互斥量
1、通過調用成員函數lock()進行上鎖,unlock()進行解鎖。不推薦實踐中直接去調用成員函數,因為調用成員函數就意味著,必須記住在每個函數出口都要去調用unlock(。C++標準庫為互斥量提供了一個RAII語法的模板類 std::lock_guard ,其會在構造的時候提供已鎖的互斥量,並在析構的時候進行解鎖,從而保證了一個已鎖的互斥量總是會被正確的解鎖。#include <mutex>頭文件
2、註意的問題
使用互斥量來保護數據,並不是僅僅在每一個成員函數中都加入一個 std::lock_guard 對象那
麽簡單;一個迷失的指針或引用,將會讓這種保護形同虛設。不過,檢查迷失指針或引用是
很容易的,只要1)沒有成員函數通過返回值或者輸出參數的形式向其調用者返回指向受保護數
據的指針或引用,2)成員函數沒有通過指針或引用的方式來調用外部沒有被保護的函數或者變量,數據就是安全的。
class some_data { int a; std::string b; public: void do_something(); };
class data_wrapper { private: some_data data; std::mutex m; public: template<typename Function> void process_data(Function func)//傳入函數func { std::lock_guard<std::mutex> l(m); func(data); // 1 傳遞“保護”數據給用戶函數 } };
some_data* unprotected; void malicious_function(some_data& protected_data) { unprotected=&protected_data; //此時函數是無保護的 }
data_wrapper x; void foo() { x.process_data(malicious_function); // 2 傳遞一個惡意函數 unprotected->do_something(); // 3 在無保護的情況下訪問保護數據,unprotected是some_date類型的 } //相當於在保護機制下,函數訪問了一個沒有被保護的外部函數.也就破壞了保護了
意味著foo能夠繞過保護機制將函數 malicious_function 傳遞進func()!
並發編程(3)線程間共享數據