1. 程式人生 > >C++11 多執行緒同步

C++11 多執行緒同步

多執行緒能提高程式的效率,但同時也帶來了相應的問題----資料競爭。當多個執行緒同時操作同一個變數時,就會出現資料競爭。出現數據競爭,一般會用臨界區(Critical Section)、互斥量(Mutex)、訊號量(Semaphore)、事件(Event)這四種方法來完成執行緒同步。

1、臨界區

對於臨界資源,多執行緒必須互斥地對它進行訪問。每個執行緒訪問臨界資源的那段程式碼就稱為臨界區。它保證每次只能有一個執行緒進入臨界區。有一個執行緒進入臨界區後其他試圖訪問臨界區的執行緒會被掛起。臨界區被釋放後,其他執行緒才可以繼續搶佔。幾種同步處理中,臨界區速度最快,但它只能實現同進程中的多個執行緒同步,無法實現多程序同步。c++11並沒有為我們提供臨界區類。

2、互斥量

互斥量與臨界區相似,但臨界區不支援多程序,而mutex支援多程序。c++11標準庫中提供了mutex類。

  1. #include "stdafx.h"
  2. #include <vector>
  3. #include <thread>
  4. #include <iostream>
  5. #include <mutex>
  6. usingnamespace std;  
  7. mutex sLock;  
  8. int i = 0;  
  9. void test()  
  10. {  
  11.     for (int j = 0; j < 1000; j++)  
  12.     {  
  13.         sLock.lock();  
  14.         i++;  
  15.         sLock.unlock();  
  16.     }  
  17. }  
  18. int main(int argc, _TCHAR* argv[])  
  19. {  
  20.     vector<thread> v;  
  21.     for (int j = 0; j < 100; j++)  
  22.     {  
  23.         v.emplace_back(test);  
  24.     }  
  25.     for(auto& t : v)  
  26.     {  
  27.         t.join();  
  28.     }  
  29.     cout << i << endl;  
  30. }  

曾有人對c++11中的thread和mutex效能進行了測試。點選開啟連結根據他的測試結果,std::thread的效能損耗不大,但std::mutex的效能損耗非常大。所以如果設計中要考慮效能的話,應該避免使用c++11標準庫中的mutex

3、訊號量

訊號量物件對執行緒的同步方式與前面幾種方法不同,訊號量允許多個執行緒同時使用共享資源。它的原理是:

P操作 申請資源: 
  (1)S減1; 
  (2)若S減1後仍大於等於零,則程序繼續執行; 
  (3)若S減1後小於零,則該程序被阻塞後進入與該訊號相對應的佇列中,然後轉入程序排程。 
V操作 釋放資源: 
  (1)S加1; 
  (2)若相加結果大於零,則程序繼續執行; 
  (3)若相加結果小於等於零,則從該訊號的等待佇列中喚醒一個等待程序,然後再返回原程序繼續執行或轉入程序排程。 

4、事件

事件物件可以通過通知操作的方式來保持執行緒同步。

******************************************************************************************************************************************

什麼是條件變數?

條件變數是一種同步機制,允許執行緒掛起,直到共享資料上的某些條件得到滿足。條件變數上的基本操作有:觸發條件(當條件變為 true 時);等待條件,掛起執行緒直到其他執行緒觸發條件。條件變數要和互斥量相聯結,以避免出現條件競爭--一個執行緒預備等待一個條件變數,當它在真正進入等待之前,另一個執行緒恰好觸發了該條件。

什麼意思呢?不清楚沒關係。看了例子就知道了:問題描述:假設有一個bool型全域性變數 isTrue ,現有10個執行緒,執行緒流程如下:當isTrue為真時,doSomething;否則掛起執行緒,直到條件滿足。那麼,用thread和mutex如何實現這個功能呢?

  1. #include <vector>
  2. #include <iostream>
  3. #include <thread>
  4. #include <mutex>
  5. #include <chrono>
  6. usingnamespace  std;  
  7. bool isTrue = false;  
  8. void doSomething()  
  9. {  
  10.     cout << "this is : " << this_thread::get_id() << endl;  
  11. }  
  12. void thread_Func()  
  13. {  
  14.     while (!isTrue)  
  15.         this_thread::yield();  
  16.     doSomething();  
  17. }  
  18. int main()  
  19. {  
  20.     vector<thread> v;  
  21.     v.reserve(10);  
  22.     for (int i = 0; i < 10; i++)  
  23.     {  
  24.         v.emplace_back(thread_Func);  
  25.     }  
  26.     this_thread::sleep_for(chrono::seconds(2));  
  27.     isTrue = true;  
  28.     for (auto& t : v)  
  29.     {  
  30.         t.join();  
  31.     }  
  32.     return 1;  
  33. }  
這段程式碼雖然能滿足需求,但有一個大缺點,就是當條件為假時,子執行緒會不停的測試條件,這樣會消耗系統資源。我們的思想是,當條件為假時,子執行緒掛起,直到條件為真時,才喚醒子執行緒。
nice,條件變數就是幹這事的!

先來看看條件變數的介紹:

條件變數能夠掛起呼叫執行緒,直到接到通知才會喚醒執行緒。它使用unique_lock<Mutex>來配合完成。下面是用條件變數實現需求:

  1. #include <iostream>
  2. #include <vector>
  3. #include <thread>
  4. #include <mutex>
  5. #include <chrono>
  6. #include <condition_variable>
  7. usingnamespace  std;  
  8. bool isTrue = false;  
  9. std::mutex mtx;  
  10. std::condition_variable cv;  
  11. void doSomething()  
  12. {  
  13.     cout << "this is : " << this_thread::get_id() << endl;  
  14. }  
  15. void thread_Func()  
  16. {  
  17.     unique_lock<mutex> loc(mtx);  
  18.     while (!isTrue)  
  19.         cv.wait(loc);  
  20.     doSomething();  
  21. }  
  22. int main()  
  23. {  
  24.     vector<thread> v;  
  25.     v.reserve(10);  
  26.     for (int i = 0; i < 10; i++)  
  27.     {  
  28.         v.emplace_back(thread_Func);  
  29.     }  
  30.     this_thread::sleep_for(chrono::seconds(2));  
  31.     {  
  32.         unique_lock<mutex> loc(mtx);  
  33.         isTrue = true;  
  34.         cv.notify_all();  
  35.     }  
  36.     for (auto& t : v)  
  37.     {  
  38.         t.join();  
  39.     }  
  40.     return 1;  
  41. }  
我們發現,在條件變數cv的wait函式中,我們傳入了一個lock引數,為什麼要用鎖呢?因為isTrue為臨界變數,主執行緒中會“寫”它,子執行緒中要“讀”它,這樣就產生了資料競爭,並且這個競爭很危險。

我們把while (!isTrue) cv.wait(loc)拆開:

1、條件判斷
2、掛起執行緒

假如現在1執行完後,時間片輪轉,該子執行緒暫停執行。而恰好在這時,主執行緒修改了條件,並呼叫了cv.notify_all()函式。這種情況下,該子執行緒是收不到通知的,因為它還沒掛起。等下一次排程子執行緒時,子執行緒接著執行2將自己掛起。但現在主執行緒中的notify早已經呼叫過了,不會再調第二次了,所以該子執行緒永遠也無法喚醒了。

為了解決上面的情況,就要使用某種同步手段來給執行緒加鎖。而c++11的condition_variable選擇了用unique_lock<Mutex>來配合完成這個功能。並且我們只需要加鎖,條件變數在掛起執行緒時,會呼叫原子操作來解鎖。

c++11還為我們提供了一個更方便的介面,連同條件判斷,一起放在條件變數裡。上面的執行緒函式可做如下修改:
  1. void thread_Func()  
  2. {  
  3. unique_lock<mutex> loc(mtx);  
  4. cv.wait(loc, []() -> bool { return isTrue;} );  
  5. doSomething();  
  6. }  

相關推薦

C++11 執行同步

多執行緒能提高程式的效率,但同時也帶來了相應的問題----資料競爭。當多個執行緒同時操作同一個變數時,就會出現資料競爭。出現數據競爭,一般會用臨界區(Critical Section)、互斥量(Mutex)、訊號量(Semaphore)、事件(Event)這四種方法來完

C++11執行程式設計 第十章: 使用packaged_task優雅的讓同步函式非同步執行

C++11 Multithreading – Part 10: packaged_task<> Example and Tutorial Varun July 2, 2017 C++11 Multithreading – Part 10: packaged_tas

[轉]c++11 執行 future/promise

[轉自 https://blog.csdn.net/jiange_zh/article/details/51602938] 1. < future >標頭檔案簡介 Classes std::future std::future_error std::packaged_task std::pro

c++11執行 thread

 1.thread建構函式 default (1) thread() noexcept; initialization (2) template <class Fn, class... Args> explicit

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

共享資料的問題 這些在作業系統中都有詳細的介紹,可以回顧作業系統課程。。很典型的就是資料競爭問題。 互斥量保護資料 最原始的方式:使用std::mutex建立互斥量,使用成員lock()加鎖,使用成員unlock()解鎖。但是這種方式需要我們在每個函數出口都呼叫一次unloc

c++11執行執行

最近需要開發一個高效能運算庫,涉及到c++多執行緒的應用,上次做類似的事情已經是4年多以前了,印象中還頗有些麻煩。悔當初做了就算了,也沒想著留點記錄什麼的。這次又研究了一番,發現用上c++11特性之後,現在已經比較簡單了,在此記錄一下。   最簡單的多執行緒情況,不涉及公共變數,各個執行緒之間獨

C++11執行(1)

        C++11中添加了duox多執行緒類,編寫C++程式可以直接使用C++11中的多執行緒庫,不必依賴於平臺多執行緒,這樣可以方便寫出誇平臺的多執行緒程式。多執行緒可以最大化利用計算機資源,提高程式碼的執行效率。         C++11中thread類提供兩

C++ 11 執行下std::unique_lock與std::lock_guard的區別和用法

這裡主要介紹std::unique_lock與std::lock_guard的區別用法 先說簡單的 一、std::lock_guard的用法 std::lock_guard其實就是簡單的RAII封裝,在建構函式中進行加鎖,解構函式中進行解鎖,這樣可以保證函式退出時,鎖一定被釋放。 簡單來說,就是防止開

C++11執行------std::async

std::async可以認為是封裝了一個std::promise,該函式返回一個std::future,用於獲取其他執行緒的資料。 一般有兩種模式: std::lanch::async:最常用的非同步模式,每次都要執行一遍 std::lanch::defer:只在第

C++11執行---互斥量、鎖、條件變數的總結

關於互斥量std::mutex的總結 互斥量用於組成程式碼的臨界區。C++的多執行緒模型是基於記憶體的,或者說是基於程式碼片段的,這和我們作業系統學習的臨界區概念基本一致,但是與Golang不同,Golang是基於訊息模型的。 一個std::mutex的lock()和unlock

C++11執行的原子操作

原子操作是同時只能有一個執行緒執行一個操作,不用使用互斥量即可實現,但是速度慢,而且一般只支援原生的型別,不夠靈活。更多的用處是作為訊號量進行使用。 示例程式碼,以int為例子: #include <atomic> #include <thread> #i

C++ 11 執行--執行管理

說到多執行緒程式設計,那麼就不得不提並行和併發,多執行緒是實現併發(並行)的一種手段。並行是指兩個或多個獨立的操作同時進行。注意這裡是同時進行,區別於併發,在一個時間段內執行多個操作。在單核時代,多個執行緒是併發的,在一個時間段內輪流執行;在多核時代,多個執行緒可以實現真正的並行,在多核上真正獨立的並行執行。

C++11執行程式設計 緒論及總結

C++11多執行緒程式設計 這一系列文章是從 https://thispointer.com/c11-multithreading-tutorial-series/ 轉過來的, 本來想翻譯一下, 但看了些內容, 用詞都不難, 讀英文沒有太大難度, 翻譯過來反而怕用詞不準畫蛇添

C++11執行程式設計 第九章: std::async 更更優雅的寫執行

C++11 Multithreading – Part 9: std::async Tutorial & Example Varun May 5, 2017 C++11 Multithreading – Part 9: std::async Tutorial &

C++11執行程式設計 第八章: 使用 std::future std::promise 更優雅的獲取執行返回值

C++11 Multithreading – Part 8: std::future , std::promise and Returning values from Thread Varun June 20, 2015 C++11 Multithreading – Part

C++11執行程式設計 第七章: 條件變數及其使用方法

C++11 Multithreading – Part 7: Condition Variables Explained Varun June 2, 2015 C++11 Multithreading – Part 7: Condition Variables Explain

C++11執行程式設計 第五章: 使用鎖來解決竟態條件

C++11 Multithreading – Part 5: Using mutex to fix Race Conditions Varun February 22, 2015 C++11 Multithreading – Part 5: Using mutex to fi

C++11執行程式設計 第四章: 共享資料和競態條件

C++11 Multithreading – Part 4: Data Sharing and Race Conditions Varun February 21, 2015C++11 Multithreading – Part 4: Data Sharing and Race Con

C++11執行程式設計 第三章: 如何向執行傳參

C++11 Multithreading – Part 3: Carefully Pass Arguments to Threads Varun January 22, 2015 C++11 Multithreading – Part 3: Carefully Pass Ar

C++11執行程式設計 第二章: join 和 detach 執行

  C++11 Multithreading – Part 2: Joining and Detaching Threads Varun January 21, 2015 C++11 Multithreading – Part 2: Joining and De