C++11併發程式設計(一)——初始C++11多執行緒庫

無鞋童鞋發表於2017-08-05

1 前言
  C++11標準在標準庫中為多執行緒提供了元件,這意味著使用C++編寫與平臺無關的多執行緒程式成為可能,而C++程式的可移植性也得到了有力的保證。
  在之前我們主要使用的多執行緒庫要麼是屬於某個單獨平臺的,例如:POSIX執行緒庫(Linux),Windows執行緒庫(Windows),還有第三方資料庫:Boost執行緒庫。但是且不說效能上的不同,它們都有各自缺點,要麼受平臺限制無法系統間移植程式,要麼需要下載第三方程式包來支援。相比較而言,我們當然更希望使用可移植的官方標準多執行緒庫,這些在C++11中提供了元件,下面我們就一同來學習學習C++11中多執行緒庫吧。
2 併發與並行的區別
  併發指的是兩個或多個獨立的活動在同一時段內發生。生活中併發的例子並不少,例如在跑步的時候你可能同時在聽音樂;在看電腦顯示器的同時你的手指在敲擊鍵盤。這時我們稱我們大腦併發地處理這些事件,只不過我們大腦的處理是有次重點的:有時候你會更關注你呼吸的頻率,而有時候你更多地被美妙的音樂旋律所吸引。這時我們可以說大腦是一種併發設計的結構。這種次重點在計算機程式設計中,體現為某一個時刻只能處理一個操作。
  與併發相近的另一個概念是並行。它們兩者存在很大的差別。並行就是同時執行,計算機在同一時刻,在某個時間點上處理兩個或以上的操作。判斷一個程式是否並行執行,只需要看某個時刻上是否多兩個或以上的工作單位在執行。一個程式如果是單執行緒的,那麼它無法並行地執行。利用多執行緒與多程式可以使得計算機並行地處理程式(當然 ,前提是該計算機有多個處理核心)。
  併發:同一時間段內可以交替處理多個操作:

這裡寫圖片描述

  並行:同一時間段內同時處理多個操作:
這裡寫圖片描述

  第一張圖中兩個任務佇列同時等待一個處理器處理,兩個佇列可能約定交替著進行被處理,也可能是大家同時競爭被處理(通訊)。後一種方式可能引起衝突:因為一個處理器無法同時進行兩步操作。但在邏輯上看來,這個處理器是同時處理這兩個佇列。
  而第二張圖中兩個任務佇列是並行處理,每個佇列都有自己的獨立處理器(或者核),兩個佇列中間沒有競爭關係,佇列中的某個排隊者只需等待佇列前面的訊息處理完成,然後再輪到自己被處理。在物理上,如果是兩個核的CPU巨集觀上看是同時處理這兩個任務佇列。
  併發的程式設計,提供了一種方式讓我們能夠設計出一種方案將問題(非必須地)並行地解決。如果我們將程式的結構設計為可以併發執行的,那麼在支援並行的機器上,我們可以將程式並行地執行。因此,併發重點指的是程式的設計結構,而並行指的是程式執行的狀態。併發程式設計,是一種將一個程式分解成小片段獨立執行的程式設計方法。
3 併發的兩種模式
  這裡兩種模式指的就是我們都知道的多執行緒併發與多程式併發的兩種方法。
 3.1 多程式併發
  多個程式獨立地執行,它們之間通過程式間常規的通訊渠道傳遞訊息(訊號,套接字,檔案,管道等),這種程式間通訊不是設定複雜就是速度慢,這是因為為了避免一個程式去修改另一個程式,作業系統在程式間提供了一定的保護措施,當然,這也使得編寫安全的併發程式碼更容易。執行多個程式也需要固定的開銷:程式的啟動時間,程式管理的資源消耗。
 3.2 多執行緒併發
  在當個程式中執行多個執行緒也可以併發。執行緒就像輕量級的程式,每個執行緒相互獨立執行,但它們共享地址空間,所有執行緒訪問到的大部分資料如指標、物件引用或其他資料可以線上程之間進行傳遞,它們都可以訪問全域性變數。程式之間通常共享記憶體,但這種共享通常難以建立且難以管理,缺少執行緒間資料的保護。因此,在多執行緒程式設計中,我們必須確保每個執行緒鎖訪問到的資料是一致的。
4 C++中的併發與多執行緒
  C++標準並沒有提供對多程式併發的原生支援,所以C++的多程式併發要靠其他API——這需要依賴相關平臺。
  C++11 標準提供了一個新的執行緒庫,內容包括了管理執行緒、保護共享資料、執行緒間的同步操作、低階原子操作等各種類。標準極大地提高了程式的可移植性,以前的多執行緒依賴於具體的平臺,而現在有了統一的介面進行實現。
  C++11 新標準中引入了幾個標頭檔案來支援多執行緒程式設計:

  • < thread > :包含std::thread類以及std::this_thread名稱空間。管理執行緒的函式和類在該標頭檔案中有宣告;
  • < atomic > :包含std::atomic和std::atomic_flag類,以及一套C風格的原子型別和與C相容的原子操作的函式;
  • < mutex > :包含了與互斥量相關的類以及其他型別和函式;
  • < future > :包含兩個Provider類(std::promise和std::package_task)和兩個Future類(std::future和std::shared_future)以及相關的型別和函式;
  • < condition_variable > :包含與條件變數相關的類,包括std::condition_variable和std::condition_variable_any。
    4.1 例項來說
      我們先從一個最簡單的也是最常見的單執行緒程式來看:
include<iostream>
using namespace std;

int main()
{
    cout<<"This is a test!"<<endl;
    return 0;
}

  上面程式就是一個單執行緒過程,main函式中列印一句話沒有多餘操作。那我們想另啟一個執行緒來完成這個活怎麼做呢?我們看下面的程式:

# include<iostream>
# include<thread>
using namespace std;

void subThread()
{
    cout<<"This is a test!"<<endl;
}

int main()
{
    thread t(subThread); // 建立另外一個執行緒
    t.join();            // 等待另一個執行緒執行完畢
}

  在上面程式中我們建立了另一個執行緒來完成列印工作。每個執行緒都必須有一個初始函式,新執行緒的執行開始於初始函式。對於第一段程式來說,它的初始函式是main,對於我們新建立的執行緒,可以在std::thread()物件的建構函式中指定。
  在第二段程式裡,程式由兩個執行緒組成:初始執行緒始於main,新執行緒始於subThread。這裡將新執行緒t的初始函式指定為subThread。
  新執行緒啟動之後會與初始程式一併執行,初始執行緒可以等待或不等待新程式的執行結束——如果需要等待執行緒,則新執行緒例項需要使用join(),否則可以使用detach()。如果不等待新執行緒,則初始執行緒自顧自地執行到main()結束。
  到此我們就建立了第一個基於C++11多執行緒庫的多執行緒程式,一般來說並不值得為了如此簡單的任務而使用多執行緒,尤其是在這期間初始執行緒並沒做什麼。後面我們會接觸更深而更有意義的程式。


  參考文獻:
  http://www.cnblogs.com/lpxblog/p/5190438.html
  http://blog.csdn.net/wrx1721267632/article/details/52197849

相關文章