鍍金池/ 教程/ C/ 1.3 C++中的并發(fā)和多線程
3.4 本章總結
6.3 基于鎖設計更加復雜的數(shù)據(jù)結構
6.1 為并發(fā)設計的意義何在?
5.2 <code>C++</code>中的原子操作和原子類型
A.7 自動推導變量類型
2.1 線程管理的基礎
8.5 在實踐中設計并發(fā)代碼
2.4 運行時決定線程數(shù)量
2.2 向線程函數(shù)傳遞參數(shù)
第4章 同步并發(fā)操作
2.3 轉移線程所有權
8.3 為多線程性能設計數(shù)據(jù)結構
6.4 本章總結
7.3 對于設計無鎖數(shù)據(jù)結構的指導建議
關于這本書
A.1 右值引用
2.6 本章總結
D.2 &lt;condition_variable&gt;頭文件
A.6 變參模板
6.2 基于鎖的并發(fā)數(shù)據(jù)結構
4.5 本章總結
A.9 本章總結
前言
第10章 多線程程序的測試和調試
5.4 本章總結
第9章 高級線程管理
5.1 內存模型基礎
2.5 識別線程
第1章 你好,C++的并發(fā)世界!
1.2 為什么使用并發(fā)?
A.5 Lambda函數(shù)
第2章 線程管理
4.3 限定等待時間
D.3 &lt;atomic&gt;頭文件
10.2 定位并發(fā)錯誤的技術
附錄B 并發(fā)庫的簡單比較
5.3 同步操作和強制排序
A.8 線程本地變量
第8章 并發(fā)代碼設計
3.3 保護共享數(shù)據(jù)的替代設施
附錄D C++線程庫參考
第7章 無鎖并發(fā)數(shù)據(jù)結構設計
D.7 &lt;thread&gt;頭文件
D.1 &lt;chrono&gt;頭文件
4.1 等待一個事件或其他條件
A.3 默認函數(shù)
附錄A 對<code>C++</code>11語言特性的簡要介紹
第6章 基于鎖的并發(fā)數(shù)據(jù)結構設計
封面圖片介紹
7.2 無鎖數(shù)據(jù)結構的例子
8.6 本章總結
8.1 線程間劃分工作的技術
4.2 使用期望等待一次性事件
8.4 設計并發(fā)代碼的注意事項
D.5 &lt;mutex&gt;頭文件
3.1 共享數(shù)據(jù)帶來的問題
資源
9.3 本章總結
10.3 本章總結
10.1 與并發(fā)相關的錯誤類型
D.4 &lt;future&gt;頭文件
3.2 使用互斥量保護共享數(shù)據(jù)
9.1 線程池
1.1 何謂并發(fā)
9.2 中斷線程
4.4 使用同步操作簡化代碼
A.2 刪除函數(shù)
1.3 C++中的并發(fā)和多線程
1.4 開始入門
第5章 C++內存模型和原子類型操作
消息傳遞框架與完整的ATM示例
8.2 影響并發(fā)代碼性能的因素
7.1 定義和意義
D.6 &lt;ratio&gt;頭文件
A.4 常量表達式函數(shù)
7.4 本章總結
1.5 本章總結
第3章 線程間共享數(shù)據(jù)

1.3 C++中的并發(fā)和多線程

通過多線程為C++并發(fā)提供標準化支持是件新鮮事。只有在C++11標準下,才能編寫不依賴平臺擴展的多線程代碼。了解C++線程庫中的眾多規(guī)則前,先來了解一下其發(fā)展的歷史。

1.3.1 C++多線程歷史

C++98(1998)標準不承認線程的存在,并且各種語言要素的操作效果都以順序抽象機的形式編寫。不僅如此,內存模型也沒有正式定義,所以在C++98標準下,沒辦法在缺少編譯器相關擴展的情況下編寫多線程應用程序。

當然,編譯器供應商可以自由地向語言添加擴展,添加C語言中流行的多線程API———POSIX標準中的C標準和Microsoft Windows API中的那些———這就使得很多C++編譯器供應商通過各種平臺相關擴展來支持多線程。這種編譯器支持一般受限于只能使用平臺相關的C語言API,并且該C++運行庫(例如,異常處理機制的代碼)能在多線程情況下正常工作。因為編譯器和處理器的實際表現(xiàn)很不錯了,所以在少數(shù)編譯器供應商提供正式的多線程感知內存模型之前,程序員們已經(jīng)編寫了大量的C++多線程程序了。

由于不滿足于使用平臺相關的C語言API來處理多線程,C++程序員們希望使用的類庫能提供面向對象的多線程工具。像MFC這樣的應用框架,如同Boost和ACE這樣的已積累了多組類的通用C++類庫,這些類封裝了底層的平臺相關API,并提供用來簡化任務的高級多線程工具。各種類和庫在細節(jié)方面差異很大,但在啟動新線程的方面,總體構造卻大同小異。一個為許多C++類和庫共有的設計,同時也是為程序員提供很大便利的設計,也就是使用帶鎖的獲取資源即初始化(RAII, Resource Acquisition Is Initialization)的習慣,來確保當退出相關作用域時互斥元解鎖。

編寫多線程代碼需要堅實的編程基礎,當前的很多C++編譯器為多線程編程者提供了對應(平臺相關)的API;當然,還有一些與平臺無關的C++類庫(例如:Boost和ACE)。正因為如此,程序員們可以通過這些API來實現(xiàn)多線程應用。不過,由于缺乏統(tǒng)一標準的支持,缺少統(tǒng)一的線程內存模型,進而導致一些問題,這些問題在跨硬件或跨平臺相關的多線程應用上表現(xiàn)得尤為明顯。

1.3.2 新標準支持并發(fā)

所有的這些隨著C++11標準的發(fā)布而改變了,新標準中不僅有了一個全新的線程感知內存模型,C++標準庫也擴展了:包含了用于管理線程(參見第2章)、保護共享數(shù)據(jù)(參見第3章)、線程間同步操作(參見第4章),以及低級原子操作(參見第5章)的各種類。

新C++線程庫很大程度上,是基于上文提到的C++類庫的經(jīng)驗積累。特別是,Boost線程庫作為新類庫的主要模型,很多類與Boost庫中的相關類有著相同名稱和結構。隨著C++標準的進步,Boost線程庫也配合著C++標準在許多方面做出改變,因此之前使用Boost的用戶將會發(fā)現(xiàn)自己非常熟悉C++11的線程庫。

如本章起始提到的那樣,支持并發(fā)僅僅是C++標準的變化之一,此外還有很多對于編程語言自身的改善,就是為了讓程序員們的工作變得更加輕松。這些內容在本書的論述范圍之外,但是其中的一些變化對于線程庫本身及其使用方式產(chǎn)生了很大的影響。附錄A會對這些特性做一些介紹。

新的C++標準直接支持原子操作,允許程序員通過定義語義的方式編寫高效的代碼,從而無需了解與平臺相關的匯編指令。這對于試圖編寫高效、可移植代碼的程序員們來說是一個好消息;編譯器不僅可以搞定具體平臺,還可以編寫優(yōu)化器來解釋操作語義,從而讓程序整體得到更好的優(yōu)化。

1.3.3 C++線程庫的效率

通常情況下,這是高性能計算開發(fā)者對C++的擔憂之一。為了效率,C++類整合了一些底層工具。這樣就需要了解相關使用高級工具和使用低級工具的開銷差,這個開銷差就是抽象代價(abstraction penalty)。

C++標準委員會在設計標準庫時,特別是設計標準線程庫的時候,就已經(jīng)注意到了這點;目的就是在實現(xiàn)相同功能的前提下,直接使用底層API并不會帶來過多的性能收益。因此,該類庫在大部分主流平臺上都能實現(xiàn)高效(帶有非常低的抽象代價)。

C++標準委員會為了達到終極性能,需要確保C++能給那些要與硬件打交道的程序員,提供足夠多的的底層工具。為了這個目的,伴隨著新的內存模型,出現(xiàn)了一個綜合的原子操作庫,可用于直接控制單個位、字節(jié)、內部線程間同步,以及所有變化的可見性。原子類型和相應的操作現(xiàn)在可以在很多地方使用,而這些地方以前可能使用的是平臺相關的匯編代碼。使用了新標準的代碼會具有更好的可移植性,而且更容易維護。

C++標準庫也提供了更高級別的抽象和工具,使得編寫多線程代碼更加簡單,并且不易出錯。有時運用這些工具確實會帶來性能開銷,因為有額外的代碼必須執(zhí)行。但是,這種性能成本并不一定意味著更高的抽象代價;總體來看,這種性能開銷并不比手工編寫等效函數(shù)高,而且編譯器可能會很好地內聯(lián)大部分額外代碼。

某些情況下,高級工具會提供一些額外的功能。大部分情況下這都不是問題,因為你沒有為你不使用的那部分買單。在罕見的情況下,這些未使用的功能會影響其他代碼的性能。如果你很看重程序的性能,并且高級工具帶來的開銷過高,你最好是通過較低級別的工具來實現(xiàn)你需要的功能。絕大多數(shù)情況下,額外增加的復雜性和出錯幾率都遠大于性能的小幅提升帶來的收益。即便是有證據(jù)確實表明瓶頸出現(xiàn)在C++標準庫的工具中,也可能會歸咎于低劣的應用設計,而非低劣的類庫實現(xiàn)。例如,如果過多的線程競爭一個互斥單元,將會很明顯的影響性能。與其在互斥操作上耗費時間,不如重新設計應用,減少互斥元上的競爭來得劃算。如何減少應用中的競爭,會在第8章中再次提及。

在C++標準庫沒有提供所需的性能或行為時,就需要使用與平臺相關的工具。

1.3.4 平臺相關的工具

雖然C++線程庫為多線程和并發(fā)處理提供了較全面的工具,但在某些平臺上提供額外的工具。為了方便地訪問那些工具的同時,又使用標準C++線程庫,在C++線程庫中提供一個native_handle()成員函數(shù),允許通過使用平臺相關API直接操作底層實現(xiàn)。就其本質而言,任何使用native_handle()執(zhí)行的操作都是完全依賴于平臺的,這超出了本書(同時也是標準C++庫本身)的范圍。

所以,使用平臺相關的工具之前,要明白標準庫能夠做什么,那么下面通過一個栗子來展示下吧。