保護共享數據的最基本的方式,是使用C++標准庫提供的互斥量(頭文件<mutex>)。當訪問共享數據前,使用互斥量將相關數據鎖住,再當訪問結束后,再將數據解鎖。線程庫需要保證,當一個線程使用特定互斥量鎖住共享數據時,其他的線程想要訪問鎖住的數據,都必須等到之前那個線程對數據進行解鎖后,才能進行訪問。這就保證了所有線程能看到共享數據,而不破壞不變量。
C++中通過實例化 std::mutex 創建互斥量,通過調用成員函數lock()進行上鎖,unlock()進行解鎖。
1 #include <iostream> 2 #include <thread> 3 #include <mutex> 4 #include <list> 5 6 using namespace std; 7 8 class A { 9 public: 10 void input() 11 { 12 for (int i = 0; i < 1000; i++) 13 { 14 my_mutex.lock(); 15 cout << "加入數據:" << i << endl; 16 ilst.push_back(i); 17 my_mutex.unlock(); 18 } 19 20 } 21 22 void output() 23 { 24 for (int i = 0; i < 1000; i++) 25 { 26 my_mutex.lock(); 27 if (!ilst.empty()) 28 { 29 cout << "讀讀讀讀出數據:" << ilst.front() << endl; 30 ilst.pop_front(); 31 my_mutex.unlock(); 32 } 33 else 34 my_mutex.unlock(); 35 } 36 } 37 38 private: 39 list<int> ilst; 40 mutex my_mutex; 41 }; 42 43 int main() 44 { 45 A a; 46 thread t1(&A::input, &a); //注意此處需要傳入的是對象地址,否則數據沒辦法共享 47 thread t2(&A::output, &a); 48 t1.join(); 49 t2.join(); 50 return 0; 51 }
C++標准庫還為互斥量提供了一個RAII語法的模板類 std::lack_guard<T> ,其會在構造的時候提供已鎖的互斥量,並在析構的時候進行解鎖,從而保證了一個已鎖的互斥量總是會被正確的解鎖。
1 #include <iostream> 2 #include <thread> 3 #include <mutex> 4 #include <list> 5 6 using namespace std; 7 8 class A { 9 public: 10 void input() 11 { 12 for (int i = 0; i < 1000; i++) 13 { 14 lock_guard<mutex> guard(my_mutex); 15 /*my_mutex.lock();*/ 16 cout << "加入數據:" << i << endl; 17 ilst.push_back(i); 18 /* my_mutex.unlock();*/ 19 } 20 21 } 22 23 void output() 24 { 25 for (int i = 0; i < 1000; i++) 26 { 27 lock_guard<mutex> guard(my_mutex); 28 /*my_mutex.lock();*/ 29 if (!ilst.empty()) 30 { 31 cout << "讀讀讀讀出數據:" << ilst.front() << endl; 32 ilst.pop_front(); 33 /*my_mutex.unlock();*/ 34 } 35 /*else 36 my_mutex.unlock();*/ 37 } 38 } 39 40 private: 41 list<int> ilst; 42 mutex my_mutex; 43 }; 44 45 int main() 46 { 47 A a; 48 thread t1(&A::input, &a); //注意此處需要傳入的是對象地址,否則數據沒辦法共享 49 thread t2(&A::output, &a); 50 t1.join(); 51 t2.join(); 52 return 0; 53 }
但互斥量自身也有問題。
當其中一個成員函數返回的是保護數據的指針或引用時,會破壞對數據的保護。具有訪問能力的指針或引用可以訪問(並可能修改)被保護的數據,而不會被互斥鎖限制。所以切勿將受保護數據的指針或引用傳遞到互斥鎖作用域之外,無論是函數返回值,還是存儲在外部可見內存,亦或是以參數的形式傳遞到用戶提供的函數中去。
另外還會造成死鎖,或是對數據保護的太多(或太少)的問題。
