C++11 并發編程基礎(一):并發、并行與C++多線程
來自: http://www.cnblogs.com/QG-whz/p/5186243.html
C++11標準在標準庫中為多線程提供了組件,這意味著使用C++編寫與平臺無關的多線程程序成為可能,而C++程序的可移植性也得到了有力的保證。另外,并發編程可提高應用的性能,這對對性能錙銖必較的C++程序員來說是值得關注的。
1. 何為并發
并發指的是兩個或多個獨立的活動在 同一時段 內發生。生活中并發的例子并不少,例如在跑步的時候你可能同時在聽音樂;在看電腦顯示器的同時你的手指在敲擊鍵盤。這時我們稱我們大腦并發地處理這些事件,只不過我們大腦的處理是有次重點的:有時候你會更關注你呼吸的頻率,而有時候你更多地被美妙的音樂旋律所吸引。這時我們可以說 大腦是一種并發設計的結構。 這種次重點在計算機程序設計中,體現為 某一個時刻只能處理一個操作 。
與并發相近的另一個概念是 并行 。它們兩者存在很大的差別。并行就是 同時執行 ,計算機在 同一時刻 ,在某個時間點上處理兩個或以上的操作。判斷一個程序是否并行執行,只需要看某個時刻上是否多兩個或以上的工作單位在運行。一個程序如果是單線程的,那么它無法并行地運行。利用多線程與多進程可以使得計算機并行地處理程序(當然 ,前提是該計算機有多個處理核心)。
- 并發:同一時間段內可以交替處理多個操作:
圖中整個安檢系統是一個 并發 設計的結構。兩個安檢隊列隊首的人競爭這一個安檢窗口,兩個隊列可能約定交替著進行安檢,也可能是大家同時競爭安檢窗口(通信)。后一種方式可能引起沖突:因為無法同時進行兩個安檢操作。在 邏輯 上看來,這個安檢窗口是同時處理這兩個隊列。
- 并行:同一時刻內同時處理多個操作:
圖中整個安檢系統是一個 并行 的系統。在這里,每個隊列都有自己的安檢窗口,兩個隊列中間沒有競爭關系,隊列中的某個排隊者只需等待隊列前面的人安檢完成,然后再輪到自己安檢。在 物理 上,安檢窗口同時處理這兩個隊列。
并發的程序設計,提供了一種方式讓我們能夠設計出一種方案將問題(非必須地)并行地解決。如果我們將程序的結構設計為可以并發執行的,那么在支持并行的機器上,我們可以將程序并行地執行。因此,并發重點指的是程序的設計結構,而并行指的是程序運行的狀態。并發編程,是一種將一個程序分解成小片段獨立執行的程序設計方法。
2.并發的基本方式途徑
多線程與多進程是并發的兩種途徑。
想象兩個場景:
- 場景一:你和小伙伴要開發一個項目,但小伙伴們放寒假都回家了,你們只能通過QQ聊天、手機通話、發送思維導圖等方式來進行交流,總之你們無法很方便地進行溝通。好處是你們各自工作時可以互不打擾。
- 場景二:你和小伙伴放假都呆在學校實驗室中開發項目,你們可以聚在一起使用頭腦風暴,可以使用白板進行觀點的闡述,總之你們溝通變得更方便有效了。有點遺憾的是你在思考時可能有小伙伴過來問你問題,你受到了打擾。
這兩個場景描繪了并發的兩種基本途徑。每個小伙伴代表一個線程,工作地點代表一個處理器。場景一中每個小伙伴是一個單線程的進程,他們擁有獨立的處理器,多個進程同時執行;場景二中只有一個處理器,所有小伙伴都是屬于同一進程的線程。
2.1 多進程并發
多個進程獨立地運行,它們之間通過進程間常規的通信渠道傳遞訊息(信號,套接字,文件,管道等),這種進程間通信不是設置復雜就是速度慢,這是因為為了避免一個進程去修改另一個進程,操作系統在進程間提供了一定的保護措施,當然,這也使得編寫安全的并發代碼更容易。運行多個進程也需要固定的開銷:進程的啟動時間,進程管理的資源消耗。
2.2 多線程并發
在當個進程中運行多個線程也可以并發。線程就像輕量級的進程,每個線程相互獨立運行,但它們共享地址空間,所有線程訪問到的大部分數據如指針、對象引用或其他數據可以在線程之間進行傳遞,它們都可以訪問全局變量。進程之間通常共享內存,但這種共享通常難以建立且難以管理,缺少線程間數據的保護。因此,在多線程編程中,我們必須確保每個線程鎖訪問到的數據是一致的。
3. 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。
3.1 初試多線程
我們從一個hello開始。在單線程時:
# include<iostream> using namespace std; int main() { cout<<"hello world"<<endl; }
在這里,進行由一個線程組成,該線程的初始函數是main。我們啟動第二個線程來打印hello world:
# include<iostream>include<thread>
using namespace std; void hello() { cout<<"hello world"<<endl; } int main() { thread t (hello); t.join(); }</pre>
在這里,我們將打印hello world的語句放在函數hello中。每個線程都必須有一個初始函數,新線程的執行開始于初始函數。對于第一段程序來說,它的初始函數是main,對于我們新創建的線程,可以在std::thread()對象的構造函數中指定。
在第二段程序里,程序由兩個線程組成:初始線程始于main,新線程始于hello。這里將新線程t的初始函數指定為hello。
新線程啟動之后會與初始進程一并運行,初始線程可以等待或不等待新進程的運行結束——如果需要等待線程,則新線程實例需要使用join(),否則可以使用detach()。如果不等待新線程,則初始線程自顧自地運行到main()結束。
關于< thread > 我們將在下一篇中進行詳解。
由于我們的初始線程并沒有做什么事情,啟動新線程后,新線程將打印出hello world。
</div>
這就是我們編寫出的第一個多線程的程序,一般來說并不值得為了如此簡單的任務而使用多線程,尤其是在這期間初始線程并沒做什么。
在下一篇文章里,我們將繼續探索 < thread >頭文件 的內容,編寫更復雜的并發程序。
</div>本文由用戶 x33189804 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!