1. 程式人生 > >C++11 多執行緒執行緒共享資料

C++11 多執行緒執行緒共享資料

共享資料的問題

這些在作業系統中都有詳細的介紹,可以回顧作業系統課程。。很典型的就是資料競爭問題。

互斥量保護資料

最原始的方式:使用std::mutex建立互斥量,使用成員lock()加鎖,使用成員unlock()解鎖。但是這種方式需要我們在每個函數出口都呼叫一次unlock(),過於繁瑣。。。
例項:

// std::lock example
#include <iostream>       // std::cout
#include <thread>         // std::thread
#include <mutex>          
// std::mutex, std::lock std::mutex foo,bar; // 全域性鎖 void task_a () { // foo.lock(); bar.lock(); // replaced by: std::lock (foo,bar); std::cout << "task a\n"; foo.unlock(); bar.unlock(); } void task_b () { // bar.lock(); foo.lock(); // replaced by: std::lock (bar,foo); std::cout <<
"task b\n"; bar.unlock(); foo.unlock(); } int main () { std::thread th1 (task_a); std::thread th2 (task_b); th1.join(); th2.join(); return 0; } /* 輸出結果: task a task b */

一般使用std::lock_guard模板類,在構造的時候對互斥量加鎖,析構時解鎖。

#include <iostream>
#include <mutex>
#include <thread>
#include <stdexcept> std::mutex mtx; void print_even(int x) { if(x % 2 == 0) std::cout << x << " is even\n"; else throw (std::logic_error("not even")); } void print_thread_id(int id) { try { std::lock_guard<std::mutex>lck(mtx); // 函式結束時,自動析構解鎖 print_even(id); // 相當於在這裡加鎖了 } catch(std::logic_error&) { std::cout << "[exception caught]\n"; } } int main() { std::thread threads[10]; for(int i = 0; i < 10; ++i) { threads[i] = std::thread(print_thread_id, i + 1); } for(auto& th : threads) th.join(); }

預防死鎖

一般都是程式設計時候進行考慮的。。。方法留作後續補充。。作業系統上的思想為指導。

注意合理組織程式碼

C++中,如果是直接對地址進行操作,那麼鎖無法阻止這種行為。

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)
  {
    std::lock_guard<std::mutex> l(m);
    func(data);    // 1 傳遞“保護”資料給使用者函式
  }
};

some_data* unprotected;

void malicious_function(some_data& protected_data)
{
  unprotected=&protected_data;  // 這裡直接獲取地址,那麼unprotected的操作無法被鎖限制
}

data_wrapper x;
void foo()
{
  x.process_data(malicious_function);    // 2 傳遞一個惡意函式
  unprotected->do_something();    // 3 在無保護的情況下訪問保護資料
}

因此程式碼中儘量避免直接對臨界區的資料進行地址操作。