內容簡介
《C++並發編程實戰》是一本基於C++11新標準的並發和多綫程編程深度指南。內容包括從std::thread、std::mutex、std::future和std::async等基礎類的使用,到內存模型和原子操作、基於鎖和鎖數據結構的構建,再擴展到並行算法、綫程管理,最後還介紹瞭多綫程代碼的測試工作。本書的附錄部分還對C++11新語言特性中與多綫程相關的項目進行瞭簡要的介紹,並提供瞭C++11綫程庫的完整參考。
《C++並發編程實戰》適閤於需要深入瞭解C++多綫程開發的讀者,以及使用C++進行各類軟件開發的開發人員、測試人員。對於使用第三方綫程庫的讀者,也可以從本書後麵的章節中瞭解到相關的指引和技巧。同時,本書還可以作為C++11綫程庫的參考工具書。
作者簡介
周全,軟件工程師,畢業於中國科學技術大學信息學院,現任職於中國人民銀行閤肥中心支行科技處。從事.NET開發多年,有較為豐富的係統集成和運維經驗,對虛擬化也有較深入的研究。可以通過email與他聯係。
宋真真,網絡工程師,2008年畢業於閤肥工業大學計算機與信息學院,現任職於中國人民銀行閤肥中心支行科技處,參與軟件開發、項目管理等工作,愛好數據庫、編程等研究。可以通過email與她聯係。
梁娟娟,2010年畢業於中國科學技術大學信息技術學院,現就職於中國人民銀行閤肥中心支行。
許敏,軟件工程師,2005年獲得軟件測試工程師證書。現任職於中國人民銀行閤肥中心支行科技處,負責項目管理工作。可以通過Email與她聯係。
目錄
第1章 你好,C++並發世界 1
1.1 什麼是並發 2
1.1.1 計算機係統中的並發 2
1.1.2 並發的途徑 3
1.2 為什麼使用並發 5
1.2.1 為瞭劃分關注點而使用
並發 5
1.2.2 為瞭性能而使用並發 6
1.2.3 什麼時候不使用並發 7
1.3 在C++中使用並發和
多綫程 8
1.3.1 C++多綫程曆程 8
1.3.2 新標準中的並發支持 9
1.3.3 C++綫程庫的效率 9
1.3.4 平颱相關的工具 10
1.4 開始入門 11
1.5 小結 12
第2章 管理綫程 13
2.1 基本綫程管理 13
2.1.1 啓動綫程 14
2.1.2 等待綫程完成 16
2.1.3 在異常環境下的等待 17
2.1.4 在後颱運行綫程 19
2.2 傳遞參數給綫程函數 20
2.3 轉移綫程的所有權 23
2.4 在運行時選擇綫程
數量 26
2.5 標識綫程 28
2.6 小結 29
第3章 在綫程間共享數據 31
3.1 綫程之間共享數據的
問題 32
3.1.1 競爭條件 33
3.1.2 避免有問題的競爭
條件 34
3.2 用互斥元保護共享
數據 35
3.2.1 使用C++中的互斥元 35
3.2.2 為保護共享數據精心組織
代碼 36
3.2.3 發現接口中固有的競爭
條件 38
3.2.4 死鎖:問題和解決方案 44
3.2.5 避免死鎖的進一步
指南 46
3.2.6 用std::unique_lock靈活
鎖定 51
3.2.7 在作用域之間轉移鎖的
所有權 52
3.2.8 鎖定在恰當的粒度 54
3.3 用於共享數據保護的替代工具 56
3.3.1 在初始化時保護共享
數據 56
3.3.2 保護很少更新的數據
結構 59
3.3.3 遞歸鎖 61
3.4 小結 62
第4章 同步並發操作 63
4.1 等待事件或其他條件 63
4.1.1 用條件變量等待條件 65
4.1.2 使用條件變量建立一個
綫程安全隊列 67
4.2 使用future等待一次性
事件 71
4.2.1 從後颱任務中返迴值 72
4.2.2 將任務與future相關聯 74
4.2.3 生成(std::)promise 77
4.2.4 為future保存異常 79
4.2.5 等待自多個綫程 80
4.3 有時間限製的等待 82
4.3.1 時鍾 83
4.3.2 時間段 84
4.3.3 時間點 85
4.3.4 接受超時的函數 86
4.4 使用操作同步來簡化
代碼 88
4.4.1 帶有future的函數式
編程 88
4.4.2 具有消息傳遞的同步
操作 92
4.5 小結 96
第5章 C++內存模型和原子
類型上操作 97
5.1 內存模型基礎 98
5.1.1 對象和內存位置 98
5.1.2 對象、內存位置以及
並發 99
5.1.3 修改順序 100
5.2 C++中的原子操作及
類型 100
5.2.1 標準原子類型 101
5.2.2 std::atomic_flag上的
操作 103
5.2.3 基於std::atomic的
操作 105
5.2.4 std::atomic上的操作:指針算術運算 107
5.2.5 標準原子整型的
操作 108
5.2.6 std::atomic<>初級類
模闆 109
5.2.7 原子操作的自由函數 111
5.3 同步操作和強製
順序 112
5.3.1 synchronizes-with
關係 114
5.3.2 happens-before關係 114
5.3.3 原子操作的內存
順序 116
5.3.4 釋放序列和
synchronizes-with 133
5.3.5 屏障 135
5.3.6 用原子操作排序非原子
操作 137
5.4 小結 138
第6章 設計基於鎖的並發
數據結構 140
6.1 為並發設計的含義是
什麼 141
6.2 基於鎖的並發數據
結構 142
6.2.1 使用鎖的綫程
安全棧 142
6.2.2 使用鎖和條件變量的綫程
安全隊列 145
6.2.3 使用細粒度鎖和條件變量的綫程安全隊列 149
6.3 設計更復雜的基於鎖的
數據結構 160
6.3.1 編寫一個使用鎖的綫程
安全查找錶 160
6.3.2 編寫一個使用鎖的綫程
安全鏈錶 165
6.4 小結 169
第7章 設計鎖的並發數據
結構 170
7.1 定義和結果 171
7.1.1 非阻塞數據結構的
類型 171
7.1.2 鎖數據結構 172
7.1.3 等待的數據結構 172
7.1.4 鎖數據結構的優點與
缺點 172
7.2 鎖數據結構的
例子 173
7.2.1 編寫不用鎖的綫程
安全棧 174
7.2.2 停止惱人的泄漏:在鎖數據結構中管理內存 178
7.2.3 用風險指針檢測不能被
迴收的結點 182
7.2.4 使用引用計數檢測
結點 189
7.2.5 將內存模型應用至
鎖棧 194
7.2.6 編寫不用鎖的綫程安全
隊列 198
7.3 編寫鎖數據結構的
準則 209
7.3.1 準則:使用std::memory_order_
seq_cst作為原型 210
7.3.2 準則:使用鎖內存迴收
模式 210
7.3.3 準則:當心ABA問題 210
7.3.4 準則:識彆忙於等待的循環以及輔助其他綫程 211
7.4 小結 211
第8章 設計並發代碼 213
8.1 在綫程間劃分工作的
技術 214
8.1.1 處理開始前在綫程間劃分
數據 214
8.1.2 遞歸地劃分數據 215
8.1.3 以任務類型劃分
工作 219
8.2 影響並發代碼性能的
因素 222
8.2.1 有多少個處理器 222
8.2.2 數據競爭和乒乓
緩存 223
8.2.3 假共享 225
8.2.4 數據應該多緊密 225
8.2.5 過度訂閱和過多的任務
切換 226
8.3 為多綫程性能設計數據
結構 226
8.3.1 為復雜操作劃分數組
元素 227
8.3.2 其他數據結構中的數據
訪問方式 228
8.4 為並發設計時的額外
考慮 230
8.4.1 並行算法中的異常
安全 230
8.4.2 可擴展性和阿姆達爾
定律 237
8.4.3 用多綫程隱藏延遲 238
8.4.4 用並發提高響應性 239
8.5 在實踐中設計並發
代碼 241
8.5.1 std::for_each的並行
實現 241
8.5.2 std::find的並行實現 243
8.5.3 std::partial_sum的並行
實現 248
8.6 總結 256
第9章 高級綫程管理 258
9.1 綫程池 259
9.1.1 簡單的綫程池 259
9.1.2 等待提交給綫程池的
任務 261
9.1.3 等待其他任務的
任務 265
9.1.4 避免工作隊列上的
競爭 267
9.1.5 工作竊取 269
9.2 中斷綫程 273
9.2.1 啓動和中斷另一個
綫程 274
9.2.2 檢測一個綫程是否被
中斷 275
9.2.3 中斷等待條件變量 276
9.2.4 中斷在std::condition_variable_ any上的等待 279
9.2.5 中斷其他阻塞調用 281
9.2.6 處理中斷 281
9.2.7 在應用退齣時中斷後颱
任務 282
9.3 總結 284
第10章 多綫程應用的測試與
調試 285
10.1 並發相關錯誤的
類型 285
10.1.1 不必要的阻塞 286
10.1.2 競爭條件 286
10.2 定位並發相關的錯誤的
技巧 288
10.2.1 審閱代碼以定位潛在的
錯誤 288
10.2.2 通過測試定位並發相關的
錯誤 290
10.2.3 可測試性設計 291
10.2.4 多綫程測試技術 292
10.2.5 構建多綫程的測試
代碼 295
10.2.6 測試多綫程代碼的
性能 297
10.3 總結 298
附錄A 附錄A C++11部分
語言特性簡明
參考 299
附錄B 並發類庫
探秘 C++ 的並行世界:駕馭現代多綫程編程 在當今軟件開發的浪潮中,單核處理器的時代已然遠去,多核處理器成為主流。這股不可逆轉的硬件趨勢,對軟件的設計和實現提齣瞭全新的挑戰與機遇。如何充分利用多核的強大計算能力,實現程序的並行執行,以提升效率、響應速度和用戶體驗,已經成為每一個 C++ 開發者必須麵對的課題。本書旨在為您揭開 C++ 並發編程的神秘麵紗,從理論基礎到實踐技巧,帶您深入理解並熟練掌握基於 C++11 及後續標準的新一代並發和多綫程編程範式。 我們理解,在 C++ 的世界裏,並發並非一個新鮮的概念,但 C++11 標準的推齣,無疑為這一領域注入瞭前所未有的活力與規範。它不僅引入瞭一套標準化的並發模型和工具,更以一種係統化的方式,讓多綫程編程不再是少數“特權”程序員纔能駕馭的復雜技術,而是能夠被廣大開發者所理解和應用的強大武器。本書正是緊密圍繞 C++11 及以上版本的特性展開,為您提供一套清晰、係統、且高度實用的學習路徑。 核心概念的深度剖析:夯實並發編程的基石 並發編程的首要任務是建立堅實的概念基礎。本書將從最基礎的綫程模型講起,深入探討進程與綫程的區彆與聯係,以及它們在操作係統中的運行機製。我們將詳細解析綫程的生命周期,包括綫程的創建、銷毀、就緒、運行、阻塞和終止等各個狀態的轉換過程,並通過直觀的示例代碼,幫助您理解綫程是如何在多個 CPU 核心上“同時”運行的。 隨後,我們將聚焦於並發編程中最具挑戰性的部分——共享資源的訪問控製。多綫程環境下,多個綫程同時訪問和修改同一份數據,極易引發數據競爭(Data Race)等難以調試的錯誤。為瞭解決這一問題,本書將係統介紹 C++ 標準庫提供的強大同步原語,包括: 互斥量(Mutexes): 深入講解 `std::mutex` 和 `std::lock_guard`、`std::unique_lock` 等 RAII(Resource Acquisition Is Initialization)封裝,闡釋如何通過鎖定機製來保證臨界區代碼的互斥訪問,避免數據衝突。我們將對比不同互斥量類型的特性,以及它們在不同場景下的適用性。 條件變量(Condition Variables): 學習如何利用 `std::condition_variable` 實現綫程間的協作與通信。當某個綫程需要等待特定條件滿足時,它可以釋放鎖並進入睡眠狀態,等待其他綫程在條件滿足時喚醒它。我們將通過生産者-消費者模型等經典案例,生動展示條件變量在解決復雜同步問題中的威力。 原子操作(Atomics): 探索 C++11 引入的 `std::atomic` 類型,它提供瞭一種無需互斥鎖即可進行安全、高效的內存操作的方式。本書將詳細介紹各種原子操作的語義(順序一緻性、釋放-獲取等),以及它們在實現高性能並發數據結構和算法中的關鍵作用。我們將通過實際例子,展示原子操作如何避免死鎖和提高性能。 除瞭上述核心同步機製,本書還將深入講解其他重要的並發概念: 內存模型(Memory Model): 理解 C++11 內存模型對於正確編寫並發程序至關重要。我們將剖析內存序(Memory Ordering)的概念,講解不同內存序的含義和影響,以及它們如何影響編譯器和處理器對指令的重排序。這將幫助您寫齣在各種硬件平颱上都能正確運行的並發代碼,避免因處理器亂序執行而産生的微妙錯誤。 Futures 和 Promises: 學習如何使用 `std::future` 和 `std::promise` 來管理異步操作的結果。這是一種聲明式的方式,讓您能夠更方便地獲取異步任務的返迴值,或者在任務完成時通知等待的綫程。我們將展示如何通過 `std::async` 簡化異步任務的啓動和結果獲取。 Tasks 和 Threads: 詳細介紹 `std::thread` 的使用,包括綫程的創建、啓動、加入(join)和分離(detach)。我們將深入探討綫程管理的最佳實踐,以及如何避免常見的綫程管理陷阱。 實際應用與模式:將理論轉化為實踐 掌握瞭並發編程的核心概念和工具,下一步就是將其應用於實際場景,解決復雜問題。本書將通過大量精心設計的實例,將理論知識與實際開發相結閤,引導您逐步掌握各種並發編程模式和技巧。 經典並發模式: 生産者-消費者模式: 通過隊列等數據結構,演示如何實現生産者綫程生産數據,消費者綫程消費數據的經典模式,並講解如何使用互斥量和條件變量來確保綫程安全和高效的協調。 讀寫鎖模式: 探討 `std::shared_mutex`(C++17 標準引入),它允許多個綫程同時讀取共享數據,但隻允許一個綫程寫入。我們將分析讀寫鎖的適用場景和性能優勢。 綫程池: 學習如何構建一個高效的綫程池,以復用綫程,減少綫程創建和銷毀的開銷,提高程序整體的吞吐量。我們將從零開始構建一個簡單的綫程池,並討論其優化方案。 Future-based 異步任務: 再次強調 `std::async` 的強大之處,通過更復雜的異步任務組閤,展示如何構建響應迅速的用戶界麵和高性能的後颱服務。 並行算法與數據結構: 並行STL算法: 隨著 C++ 標準的不斷發展,STL(Standard Template Library)也逐步引入瞭對並行算法的支持。本書將介紹如何利用並行 STL 算法(如 `std::for_each` 的並行版本,如果可用)來加速對大型數據集的操作,例如排序、搜索和轉換等。 並發容器: 探討 C++ 標準庫中一些綫程安全的數據結構,例如 `std::unordered_map` 或 `std::vector` 在配閤適當的同步機製使用時,如何實現綫程安全。我們也將觸及一些第三方庫提供的更高級的並發容器。 異常處理與調試: 並發環境下的異常處理: 並發程序中的異常處理比單綫程程序更為復雜。本書將指導您如何正確處理綫程中的異常,以及如何在異步操作中捕獲和傳播異常。 調試並發程序: 並發程序的調試是齣瞭名的睏難。本書將分享一套行之有效的調試策略和工具,幫助您定位和解決數據競爭、死鎖、競態條件等棘手的並發問題。我們將介紹一些常用的調試技巧,例如使用斷點、日誌記錄、以及專門的並發調試工具。 進階主題與最佳實踐:走嚮精通 在掌握瞭並發編程的基礎和常用模式之後,本書還將帶領您探索更深入的進階主題,幫助您寫齣更健壯、更高效、更易於維護的並發代碼。 性能優化: 避免不必要的鎖: 詳細分析鎖的開銷,以及如何通過更細粒度的鎖、無鎖數據結構或原子操作來減少鎖的競爭,提升性能。 綫程局部存儲(Thread-Local Storage): 學習如何使用 `thread_local` 關鍵字,為每個綫程提供獨立的變量副本,避免綫程間的共享和同步開銷。 CPU 緩存與僞共享(False Sharing): 深入理解 CPU 緩存的工作原理,以及僞共享如何影響並發程序的性能,並提供相應的規避策略。 現代 C++ 特性: Lambda 錶達式與並發: Lambda 錶達式是 C++11 的一大亮點,本書將展示如何利用 Lambda 錶達式來簡化並發任務的編寫,提高代碼的可讀性和靈活性。 智能指針與綫程安全: 探討 `std::shared_ptr` 和 `std::unique_ptr` 在並發環境下的使用注意事項,以及如何確保智能指針的綫程安全。 C++20 及後續標準中的並發新特性: 展望 C++ 未來標準在並發編程領域可能帶來的新特性和改進,讓您的知識保持前沿。 並發設計的原則與模式: Actor 模型: 介紹 Actor 模型作為一種高度可擴展的並發模型,它通過消息傳遞而非共享內存來實現通信,從而簡化瞭並發編程的復雜性。 CSP (Communicating Sequential Processes): 討論 CSP 模型,以及它如何通過通道(Channel)來實現並發進程之間的通信。 設計可擴展且易於維護的並發係統: 總結一套行之有效的並發設計原則,包括如何分解問題、如何抽象並發邏輯、如何進行測試和重構等,幫助您構建健壯且長期的並發解決方案。 學習本書,您將獲得: 紮實的理論基礎: 深刻理解 C++ 並發編程的核心概念、內存模型和同步機製。 實用的技能: 熟練掌握 C++11 及以上標準提供的並發工具,並能靈活應用於各種實際場景。 解決復雜問題的能力: 能夠獨立設計、實現和調試復雜的並發程序。 提升代碼性能與健壯性: 寫齣更高效、更穩定、更能應對多核環境的 C++ 代碼。 前沿的知識: 瞭解最新的 C++ 並發編程技術和最佳實踐。 無論您是希望提升現有 C++ 項目的性能,還是打算開發高性能的並發應用程序,本書都將是您不可或缺的學習夥伴。讓我們一起踏上這段激動人心的 C++ 並發編程之旅,駕馭現代處理器的強大力量,創造更智能、更高效的軟件!