c++11多線程庫

來源:互聯網
上載者:User
文章目錄
  • 1. 通過全域函數建立線程
  • 2. 通過函數對象建立線程
  • 3. 通過lambda運算式建立線程
  • 4. 通過成員函數建立線程
  • 1. 互斥體類
  • 2. 鎖類
  • 3. 獲得多個互斥體對象上的鎖
  • 4. std::call_once         std::once_flag
  • 5. 執行個體:
  • 1. std::condition_variable  只能等待unique_lock<mutex>的條件變數
  • 2. std::condition_variable_any      支援任何類型的Lock類
  • 3.

 

 

標準線程庫,c++11引入,包含原子操作庫、互斥鎖、條件變數。。。

 

一、線程庫<thread>

 

建立線程的四種方法:

 

1. 通過全域函數建立線程

線程類的建構函式是變參建構函式,第一個參數是線程函數,後面的參數為線程函數的參數(參數通過值傳遞方式,若要引用傳遞須加std::ref())。

thread   t1 (counter, 1, 6);   //void counter(int, int);

 

2. 通過函數對象建立線程

//class Counter 實現 operator()

1) thread   t1{Counter(1, 20)};   //c++統一推薦方法

2) Counter    c(1, 20);

   thread   t2(c);

3) thread   t3(Counter(1,20));

 

比較第一種和第三種構造方式,如果函數對象的建構函式不需要任何參數。 thread  t3(Counter());是不行的,因為編譯器會認為你在聲明一個函數,函數名為t3,此時只能用第一種構造方式。

 

3. 通過lambda運算式建立線程

目前還沒學習lambda運算式,略過......

 

4. 通過成員函數建立線程

// class Counter  c();

thread   t{&Counter::process, &c};

一般常見的是一個類自己建立一個幕後處理線程:thread   t{&Counter::process, this};

 

執行緒區域儲存 thread_localthread_local    int   n;

n作為線程參數傳遞給線程,那麼每個線程有一個n的副本,線上程整個生命週期中存在,且只初始化一次,如同static局部變數。

 

 

二、原子操作庫<atomic>

 

多線程編程經常需要操作共用的記憶體,在讀/寫過程中會導致競爭條件。

例如:

int   counter = 0;

............

++counter;  //因為++counter不時原子操作,多個線程中出現此操作時不是安全執行緒的。

 

應該用:

atomic<int>   counter(0);  //等效於 atomic_int   counter(0);

............

++counter;  //此時多個線程執行++counter是一個原子操作,是安全執行緒的。

例:void   func( std::atomic<int>&   counter){  for( int   i=0;   i<1000;   ++i )    ++counter;}int   main(){  std::atomic<int>   counter(0);  std::vector<std::thread>   threads;  for( int i=0;   i<10;   ++i )    //線程參數總是值傳遞,若要傳遞引用,須加std::ref()。(標頭檔<functional>中)    threads.push_back( std::thread{ func, std::ref(counter)} );  for( auto&   t  :  threads )    t.join();    //調用join,如果線程未結束,則main函數阻塞於此。  std::count<<"Result="<<counter<<std::endl;  return 0;}/*join的調用會導致調用線程阻塞於此,若不希望調用線程阻塞,但又想知道被調線程是否結束,應當用其它方式,例如訊息...*/

 

 

三、互斥 <mutex>

編寫多線程必須分外留意操作順序,如果無法避免線程共用資料,則必須提供同步機制,保證一次只有一個線程能更改資料。使用互斥解決競爭條件,可能導致死結。

1. 互斥體類

1) 非定時互斥體類   std::mutex         std::recursive_mutex

  lock() : 嘗試擷取鎖,並且阻塞直到擷取鎖。

  try_lock() : 嘗試擷取鎖,並立即返回,成功擷取返回true,否則false。

  unlock() : 釋放鎖。

mutex與recursive_mutex的區別在於,前者已經獲得所後不得再嘗試擷取,這會死結,後者能遞迴擷取,注意釋放次數應與擷取次數相等。

 

2) 定時互斥鎖類   std::timed_mutex     std::recursive_timed_mutex

  lock() ,      try_lock() ,        unlock()

  try_lock_for(rel_time) : 指定相對時間內獲得返回true, 逾時返回false。

  try_lock_until(abs_time) : 指定系統絕對時間內獲得返回true, 逾時返回false。

timed_mutex與recursive_timed_mutex區別同上。

 

2. 鎖類

鎖類是一個封裝器,解構函式會自動釋放關聯的互斥體。

1) 簡單鎖   std::lock_guard

  其建構函式會要求獲得互斥體,並阻塞直到獲得鎖。

 

2) 複雜鎖   std::unique_lock 

     explict   unique_lock( mutex_type&   m); //阻塞直到獲得鎖。  unique_lock(mutex_type&   m,   defer_lock_t)  noexcept; //儲存一個互斥體引用,不會立即嘗試獲得鎖。鎖可以在以後獲得。  unique_lock(mutex_type&   m,   try_to_lock_t); //嘗試獲得引用的互斥鎖,未能獲得也不阻塞。  unique_lock(mutex_type&   m,   adopt_lock_t); //該鎖假定線程獲得引用的互斥鎖,並負責管理這個鎖。  template<class Clock,   class Duration>  unique_lock(mutex&  m,  const  chrono::time_point<Clock, Duration>&  abs_time); //嘗試擷取該鎖,直到超過給定的絕對時間。  template<class Rep,   class Period>  unique_lock(mutex&  m,  const  chrono::duration<Rep, Period>&  rel_time); //嘗試擷取該鎖,直到超過給定的相對時間。

unique_lock類還支援lock(), try_lock(), try_lock_for(), try_lock_until()等方法。

通過owns_lock()查看是否獲得了這個鎖;也可以用if對unique_lock對象直接判斷是否獲得鎖,因為它定義了bool()運算子。

 

3. 獲得多個互斥體對象上的鎖

1) 泛型lock可變參數模板函數

  template <class L1,  class L2,  class...L3>

  void  lock(L1&,  L2&,  L3&...);

按順序鎖定,如果一個互斥體拋出異常,會對已獲得的鎖unlock。

 

2) 泛型try_lock

  template <class L1,  class L2,  class...L3>

  int  try_lock(L1&,  L2&,  L3&...);

通過順序調用互斥體對象的try_lock,成功返回-1,失敗返回從0開始的位置索引,並對已獲得的鎖unlock。

 

參數順序每次應保持一致, 否則易死結。

 

4. std::call_once         std::once_flag

保證call_once調度的函數只被執行一次。

 

5. 執行個體:
// 1. 簡單鎖mutex   mMutex;lock_guard<mutex> mLock(mMutex);// 2. 定時鎖timed_mutex   mTimeMutex;unique_lock<timed_mutex> mLock(mTimedMutex, chrono::milliseconds(200));// 3. 泛型mutex   mut1;mutex   mut2;unique_lock<mutex>  lock1(mut1, defer_lock_t());unique_lock<mutex>  lock2(mut2, defer_lock_t());lock(lock1, lock2);// 4. 雙重檢查鎖定演算法 (代替call_once的用法)class MyClass{public:     void init() { p = new int(0);   cout<<"Init"<<endl;}private:     int *p;}MyClass  var;bool  initialized = false;mutex   mut;void  func() {     if( ! initialized)               //一次檢查     {           unique_lock<mutex>  lock1(mut);           if( ! initialized)         //兩次檢查           {                 var.init();                 initialized  = true;           }     }     cout<<"OK"<<endl;}//兩次檢查initialized。獲得鎖之前和獲得鎖之後,確保init只調用一次。

 

四、條件變數 <condition_variable>1. std::condition_variable  只能等待unique_lock<mutex>的條件變數
 notify_one();  //喚醒等待這個條件變數的線程之一 notify_all();    //喚醒所有等待這個條件變數的線程 // 1)前提是已經獲得lk的鎖 // 2)調用wait會unlock  lk,然後等待 // 3)當被喚醒後 lock  lk wait( unique_lock<mutex>& lk);   wait_for(unique_lock<mutex>& lk, const chrono::duration<Rep,Period>& rel_time); wait_until(unique_lock<mutex>&lk, const chrono::time_point<Clock,Duration>& abs_time);
2. std::condition_variable_any      支援任何類型的Lock類3.
//例:向隊列中加入資料,當隊列不為空白時,後台線程被喚醒處理資料std::queue<std::string> mQueue;std::mutex                   mMutex;std::condition_variable   mCondVar;//向隊列加入資料的線程unique_lock<mutex>  lock(mMutex);mQueue.push( data);mCondVar.notify_all();//幕後處理資料的線程unique_lock<mutex>  lock(mMutex);while(true){     //1.先釋放lock  2.然後等待被喚醒  3.被喚醒後等待擷取lock     mCondVar.wait(lock);     // process...}

 

五、 futurepromise/future模型方便擷取線程返回的結果、線程間通訊、處理異常

 

 

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.