├── LICENSE ├── README.md ├── Table-of-contents.md ├── code └── chapter1 │ └── hello-world │ ├── Makefile │ └── hello-world.cc ├── images ├── chapter1 │ ├── concurrent-vs-parallel.png │ ├── intel-blog-concurrency.png │ ├── intel-blog-parallel.png │ ├── process-memory-layout.png │ └── program-memory-layout.png ├── chapter8 │ ├── its-lock-free.png │ └── lock-free-intro.png └── chapter9 │ └── object-layout.png └── zh ├── appendix C++11 standards ├── C++11 C++ standard library changes.md ├── C++11 Core language build time performance enhancements.md ├── C++11 Core language functionality improvements.md ├── C++11 Core language runtime performance enhancements.md ├── C++11 Core language usability enhancements.md ├── README.md └── web-resources.md ├── chapter1-Introduction ├── 1.1 What is concurrency.md ├── Cplusplus-Concurrency-Introduction.md ├── README.md └── web-resources.md ├── chapter10-Concurrent-Data-Structure ├── README.md └── web-resources.md ├── chapter11-Application ├── 11.1 Producer-Consumer-solution.md └── web-resources.md ├── chapter2-Thread-Libraries ├── 2.1 Pthread.md ├── 2.2 Windows multithreading.md ├── 2.3 Multithreading-libraries-comparison.md └── web-resources.md ├── chapter3-Thread ├── Introduction-to-Thread.md ├── README.md └── web-resources.md ├── chapter4-Mutex ├── 4.1 Mutex-header-synopsis.md ├── 4.2 Mutex-tutorial.md ├── 4.3 Lock-tutorial.md ├── 4.4 Auxiliary-function.md ├── 4.5 Mutex vs pthread.md ├── README.md └── web-resources.md ├── chapter5-Condition-Variable ├── 5.1 Condition-variable header synopsis.md ├── 5.2 Condition-variable-tutorial.md ├── 5.3 Auxiliary-function.md ├── 5.4 Thread-synchronization-with-condition-variable.md ├── 5.5 Condition-variable vs pthread.md ├── README.md └── web-resources.md ├── chapter6-Future ├── 6.1 Future-header-synopsis.md ├── 6.2 Providers-tutorial.md ├── 6.3 Providers-tutorial-2.md ├── 6.4 Future-tutorial.md ├── 6.5 Auxiliary-types.md ├── 6.6 Auxiliary-function.md ├── 6.7 Future-multithreading-application.md ├── README.md └── web-resources.md ├── chapter7-Atomic ├── 7.1 Atomic-header-synopsis.md ├── 7.2 Atomic-flag-tutorial.md ├── 7.3 Atomic-tutorial.md ├── 7.4 Atomic-tutorial2.md ├── 7.5 C-style-atomic.md ├── README.md └── web-resources.md ├── chapter8-Memory-Model ├── 8.1 C++-program-layout.md ├── 8.2 C++11 memory-model-explained.md ├── 8.3 Synchronization-and-ordering.md ├── Memory-ordering-in-C++11.md ├── README.md └── web-resources.md └── chapter9-Advanced-Thread-Management ├── README.md └── web-resources.md /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 傅海平 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 写作动机 # 2 | 3 | 本书《C++ 并发编程指南》是个人在空余时间写的,由于时间仓促,加上自身水平有限,不可能写的很完善,也难免出现错误,如果你发现本书中的错误,或者有更好的想法,欢迎给我反馈,我会第一时间给予答复。后续我会坚持完善这一系列的文章。也希望感兴趣的同学和我一起完成。 4 | 5 | 本书的创作出于以下两个目的: 6 | 7 | - 传播知识,介绍 C++ 并发编程。目前国内还没有一本完整介绍 C++11 并发编程的中文书籍,希望本书可以帮助广大的 C++ 开发者学习并发编程,降低 C++ 并发编程的学习难度,减少初学者对 C++ 并发编程的恐惧感。 8 | - 自我激励和提高。创作本书的另一个目的是自我激励和提高。和大部分人一样,本人也是 C++ 菜鸟一枚,在创作本书的过程中我会查阅大量资料,时间长了,自己学到的东西就慢慢积累多了。同时,在写作的时候为了表达清楚和准确,个别语句我会不断推敲,对自己的思维和语言表达能力很有帮助的。最后我坚信: 菜鸟都一样(因为什么都不会),牛人各有各的不同。 9 | 10 | 11 | > 本书还处于创作早期(2013 年 8 月 31 日提交第一行内容),还有很多内容需要修正和完善,如果你对此感兴趣,希望能加入到我们的队伍中(虽然目前还只有我一人孤身奋战)一起来完成 《C++ 并发编程指南》,传播知识,方便他人,提高自我吧。 12 | 13 | 14 | # 本书目录(只列出一级目录) # 15 | 16 | ### 第一章 并发编程基础 ### 17 | ### 第二章 几种常见的多线程库介绍 ### 18 | ### 第三章 线程详解 ### 19 | ### 第四章 互斥量与锁 ### 20 | ### 第五章 条件变量与线程同步 ### 21 | ### 第六章 异步任务详解 ### 22 | ### 第七章 原子类型详解 ### 23 | ### 第八章 C++11 内存模型 ### 24 | ### 第九章 高级线程管理 ### 25 | ### 第十章 如何编写正确的并发数据结构 ### 26 | ### 第十一章 并发编程应用实例 ### 27 | ### 附录 C++11 新标准概览 ### 28 | 29 | 本书的详细目录和完成情况请移步[《C++ 并发编程指南》目录](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/Table-of-contents.md) 30 | 31 | # 本书创作发起人 # 32 | 33 | > forhappy(haipingf AT gmail DOT com) 34 | 35 | 微博: @傅海平ICT([www.weibo.com/1702076100](http://www.weibo.com/1702076100)) 36 | 37 | 38 | # 贡献者名单 # 39 | 40 | > 欢迎申请加入该名单 ;-) 41 | 42 | 43 | # License # 44 | 45 | The MIT License (MIT) 46 | 47 | Copyright (c) 2013 傅海平 48 | 49 | Permission is hereby granted, free of charge, to any person obtaining a copy of 50 | this software and associated documentation files (the "Software"), to deal in 51 | the Software without restriction, including without limitation the rights to 52 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 53 | the Software, and to permit persons to whom the Software is furnished to do so, 54 | subject to the following conditions: 55 | 56 | The above copyright notice and this permission notice shall be included in all 57 | copies or substantial portions of the Software. 58 | 59 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 60 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 61 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 62 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 63 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 64 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 65 | -------------------------------------------------------------------------------- /Table-of-contents.md: -------------------------------------------------------------------------------- 1 | # C++ 并发编程指南 # 2 | 本书计划分为 11 章, 分别如下安排: 3 | 4 | ## 第一章 并发编程基础 ## 5 | 6 | ### 1.1 [什么是并发编程](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter1-Introduction/1.1%20What%20is%20concurrency.md) ### 7 | ### 1.2 [并发与并行的区别和联系](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter1-Introduction/1.1%20What%20is%20concurrency.md#12-%E5%B9%B6%E5%8F%91%E4%B8%8E%E5%B9%B6%E8%A1%8C%E7%9A%84%E8%81%94%E7%B3%BB%E5%92%8C%E5%8C%BA%E5%88%AB) ### 8 | ### 1.3 为什么需要并发编程 ### 9 | ### 1.4 并发编程应用场景和经典示例 ### 10 | ### 1.5 [C++ 并发编程初探](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter1-Introduction/Cplusplus-Concurrency-Introduction.md) ### 11 | ### 1.6 [资料汇](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter1-Introduction/web-resources.md) ### 12 | 13 | 14 | ## 第二章 几种常见的多线程库介绍 ## 15 | 16 | ### 2.1 Pthread 多线程编程指南 ### 17 | 18 | ### 2.2 Windows 多线程编程指南 ### 19 | 20 | ### 2.3 几种常见的多线程库接口对比 ### 21 | 22 | ### 2.4 资料汇 ### 23 | 24 | 25 | ## 第三章 线程详解 ## 26 | 27 | ### 3.1 [`` 头文件摘要](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter3-Thread/Introduction-to-Thread.md#thread-%E5%A4%B4%E6%96%87%E4%BB%B6%E6%91%98%E8%A6%81) ### 28 | 29 | ### 3.2 [`std::thread` 详解](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter3-Thread/Introduction-to-Thread.md#stdthread-%E8%AF%A6%E8%A7%A3) ### 30 | 31 | ### 3.3 [`std::this_thread` 命名空间中相关辅助函数介绍](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter3-Thread/Introduction-to-Thread.md#stdthis_thread-%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4%E4%B8%AD%E7%9B%B8%E5%85%B3%E8%BE%85%E5%8A%A9%E5%87%BD%E6%95%B0%E4%BB%8B%E7%BB%8D) ### 32 | 33 | ### 3.4 `std::thread` 与 Pthread 线程对比 ### 34 | 35 | ### 3.5 [资料汇](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter3-Thread/web-resources.md "资料汇") ### 36 | 37 | 38 | ##第四章 互斥量与锁 ## 39 | 40 | ### 4.1 [`` 头文件摘要](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter4-Mutex/4.1%20Mutex-header-synopsis.md) ### 41 | 42 | ### 4.2 [互斥量详解](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter4-Mutex/4.2%20Mutex-tutorial.md) ### 43 | 44 | ### 4.3 [锁类型详解](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter4-Mutex/4.3%20Lock-tutorial.md) ### 45 | 46 | ### 4.4 辅助函数介绍 ### 47 | 48 | ### 4.5 `std::mutex` 与 Pthread 互斥量对比 ### 49 | 50 | ### 4.6 [资料汇](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter4-Mutex/web-resources.md) ### 51 | 52 | 53 | ##第五章 条件变量与线程同步 ## 54 | 55 | ### 5.1 [`` 头文件摘要](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter5-Condition-Variable/5.1%20Condition-variable%20header%20synopsis.md) ### 56 | 57 | ### 5.2 [条件变量详解](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter5-Condition-Variable/5.2%20Condition-variable-tutorial.md) ### 58 | 59 | ### 5.3 [辅助函数介绍](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter5-Condition-Variable/5.3%20Auxiliary-function.md) ### 60 | 61 | ### 5.4 利用条件变量(std::condition_variable)进行线程同步 ### 62 | 63 | ### 5.5 `std::condition_variable` 与 Pthread 条件变量对比 ### 64 | 65 | ### 5.6 [资料汇](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter5-Condition-Variable/web-resources.md) ### 66 | 67 | 68 | ##第六章 异步任务详解 ## 69 | 70 | ### 6.1 [`` 头文件摘要](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter6-Future/6.1%20Future-header-synopsis.md) ### 71 | 72 | ### 6.2 [异步任务提供者(Provider) 介绍](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter6-Future/6.2%20Providers-tutorial.md) ### 73 | 74 | ### 6.3 [异步任务提供者(Provider) 介绍(续)](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter6-Future/6.3%20Providers-tutorial-2.md) ### 75 | 76 | ### 6.4 [异步任务 Future 类型详解](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter6-Future/6.4%20Future-tutorial.md) ### 77 | 78 | ### 6.5 [与异步任务相关的类型介绍](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter6-Future/6.5%20Auxiliary-types.md) ### 79 | 80 | ### 6.6 [异步任务辅助函数 `std::async` 介绍](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter6-Future/6.6%20Auxiliary-function.md) ### 81 | 82 | ### 6.7 异步任务与多线程实例 ### 83 | 84 | ### 6.8 [资料汇](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter6-Future/web-resources.md) ### 85 | 86 | 87 | ##第七章 原子类型详解 ## 88 | 89 | ### 7.1 [`` 头文件摘要](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter7-Atomic/7.1%20Atomic-header-synopsis.md) ### 90 | 91 | ### 7.2 [`std::atomic_flag` 详解](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter7-Atomic/7.2%20Atomic-flag-tutorial.md) ### 92 | 93 | ### 7.3 [基本 `std::atomic` 类型详解](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter7-Atomic/7.3%20Atomic-tutorial.md) ### 94 | 95 | ### 7.4 [特化的 `std::atomic` 类型详解](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter7-Atomic/7.4%20Atomic-tutorial2.md) ### 96 | 97 | ### 7.5 [C 风格的原子操作](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter7-Atomic/7.5%20C-style-atomic.md) ### 98 | 99 | ### 7.6 C++11 内存模型初探 ### 100 | 101 | ### 7.7 如何利用原子类型设计并发数据结构 ### 102 | 103 | ### 7.8 [资料汇](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter7-Atomic/web-resources.md) ### 104 | 105 | 106 | ##第八章 C++11 内存模型 ## 107 | 108 | ### 8.1 C++ 内存模型概述 ### 109 | ### 8.2 X86 CPU 处理器架构与常见的存储一致性模型简介 ### 110 | ### 8.3 内存序(Memory Order)与同步操作 ### 111 | ### 8.4 原子类型编程实例 ### 112 | ### 8.5 Lock-free 编程初探 ### 113 | ### [资料汇](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter8-Memory-Model/web-resources.md) ### 114 | 115 | 116 | ##第九章 高级线程管理 ## 117 | 118 | 119 | ##第十章 如何编写正确的并发数据结构 ## 120 | 121 | 122 | ##第十一章 并发编程应用实例 ## 123 | 124 | ### 11.1 [利用 C++11 并发设施解决生产者消费者问题](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter11-Application/11.1%20Producer-Consumer-solution.md "如何利用 C++11 并发设施解决生产者消费者问题") ### 125 | 126 | 127 | ##附录 C++11 新标准概览 ## 128 | 129 | ### 1. [核心语言的运行时性能强化](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/appendix%20C%2B%2B11%20standards/C%2B%2B11%20Core%20language%20runtime%20performance%20enhancements.md) ### 130 | 131 | 本小节主要包括: 132 | 133 | 1. 右值引用和 move 语义 134 | 2. 泛化的常量表达式 constexpr 135 | 3. 对 POD 类型定义的修正。 136 | 137 | ### 2. [核心语言的构建时性能强化](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/appendix%20C%2B%2B11%20standards/C%2B%2B11%20Core%20language%20build%20time%20performance%20enhancements.md) ### 138 | 139 | 本小节主要包括: 140 | 141 | 1. 外部模板。 142 | 143 | ### 3. [核心语言的可用性强化](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/appendix%20C%2B%2B11%20standards/C%2B%2B11%20Core%20language%20usability%20enhancements.md) ### 144 | 145 | 本小节主要包括: 146 | 147 | 1. 初始化列表(`std::initializer_list`) 148 | 2. 统一的初始化方式 149 | 3. 类型推导(auto 和 decltype 关键字) 150 | 4. 基于范围的 for 循环 151 | 5. lambda 表达式 152 | 6. 另一种可选的函数语法 153 | 7. 对象创建优化 154 | 8. 显式虚函数重载 155 | 9. 空指针常量(nullptr) 156 | 10. 强类型枚举 157 | 11. 右尖括号(>) 158 | 12. 显式类型转换操作符 159 | 13. 模板别名 160 | 14. 无限制 union。 161 | 162 | ### 4. 核心语言的功能提升 ### 163 | 164 | 本小节主要包括: 165 | 166 | 1. 变长参数模板 167 | 2. 新的字符串字面值 168 | 3. 用户自定义的字面值 169 | 4. 多线程内存模型 170 | 5. 线程本地存储 171 | 6. 显式地使用或禁用某些特殊成员函数(构造函数,拷贝构造,赋值操作符,析构等) 172 | 7. long long int类型 173 | 8. 静态断言 assertions 174 | 9. 允许 sizeof 运算符作用在类型的数据成员上,无须明确的对象 175 | 10. 垃圾回收机制 176 | 11. 属性 177 | 178 | ### 5. C++ 标准库的变更 ### 179 | 180 | 本小节主要包括: 181 | 182 | 1. 标准库组件上的升级 183 | 2. 多线程支持 184 | 3. 元组(tuple)类型 185 | 4. 散列表(hash table) 186 | 5. 正则表达式 187 | 6. 通用智能指针 188 | 7. 可扩展的随机数功能 189 | 8. 包装引用 190 | 9. 多态函数对象包装器 191 | 10. 用于元编程的类型属性 192 | 11. 用于计算函数对象返回类型的统一方法 -------------------------------------------------------------------------------- /code/chapter1/hello-world/Makefile: -------------------------------------------------------------------------------- 1 | all:hello-world 2 | 3 | CC=g++ 4 | CPPFLAGS=-Wall -std=c++11 -ggdb 5 | LDFLAGS=-pthread 6 | 7 | hello-world:hello-world.o 8 | $(CC) $(LDFLAGS) -o $@ $^ 9 | 10 | hello-world.o:hello-world.cc 11 | $(CC) $(CPPFLAGS) -o $@ -c $^ 12 | 13 | .PHONY: 14 | clean 15 | 16 | clean: 17 | rm hello-world.o hello-world 18 | -------------------------------------------------------------------------------- /code/chapter1/hello-world/hello-world.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * ======================================================================== 3 | * 4 | * Filename: hello-world.cc 5 | * 6 | * Description: std::thread hello world example. 7 | * 8 | * Created: 09/14/2013 09:41:12 AM 9 | * 10 | * Author: Fu Haiping (forhappy), haipingf@gmail.com 11 | * Company: ICT ( Institute Of Computing Technology, CAS ) 12 | * 13 | * ======================================================================== 14 | */ 15 | 16 | #include 17 | #include 18 | #include // std::cout 19 | #include // std::thread 20 | 21 | void thread_task() { 22 | std::cout << "hello thread" << std::endl; 23 | } 24 | 25 | int main(int argc, const char *argv[]) 26 | { 27 | std::thread t(thread_task); 28 | t.join(); 29 | 30 | return EXIT_SUCCESS; 31 | } 32 | -------------------------------------------------------------------------------- /images/chapter1/concurrent-vs-parallel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ewenwan/Cplusplus-Concurrency-In-Practice/8ae68b763434c6babda553d65449e9158d63e357/images/chapter1/concurrent-vs-parallel.png -------------------------------------------------------------------------------- /images/chapter1/intel-blog-concurrency.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ewenwan/Cplusplus-Concurrency-In-Practice/8ae68b763434c6babda553d65449e9158d63e357/images/chapter1/intel-blog-concurrency.png -------------------------------------------------------------------------------- /images/chapter1/intel-blog-parallel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ewenwan/Cplusplus-Concurrency-In-Practice/8ae68b763434c6babda553d65449e9158d63e357/images/chapter1/intel-blog-parallel.png -------------------------------------------------------------------------------- /images/chapter1/process-memory-layout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ewenwan/Cplusplus-Concurrency-In-Practice/8ae68b763434c6babda553d65449e9158d63e357/images/chapter1/process-memory-layout.png -------------------------------------------------------------------------------- /images/chapter1/program-memory-layout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ewenwan/Cplusplus-Concurrency-In-Practice/8ae68b763434c6babda553d65449e9158d63e357/images/chapter1/program-memory-layout.png -------------------------------------------------------------------------------- /images/chapter8/its-lock-free.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ewenwan/Cplusplus-Concurrency-In-Practice/8ae68b763434c6babda553d65449e9158d63e357/images/chapter8/its-lock-free.png -------------------------------------------------------------------------------- /images/chapter8/lock-free-intro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ewenwan/Cplusplus-Concurrency-In-Practice/8ae68b763434c6babda553d65449e9158d63e357/images/chapter8/lock-free-intro.png -------------------------------------------------------------------------------- /images/chapter9/object-layout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ewenwan/Cplusplus-Concurrency-In-Practice/8ae68b763434c6babda553d65449e9158d63e357/images/chapter9/object-layout.png -------------------------------------------------------------------------------- /zh/appendix C++11 standards/C++11 C++ standard library changes.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ewenwan/Cplusplus-Concurrency-In-Practice/8ae68b763434c6babda553d65449e9158d63e357/zh/appendix C++11 standards/C++11 C++ standard library changes.md -------------------------------------------------------------------------------- /zh/appendix C++11 standards/C++11 Core language build time performance enhancements.md: -------------------------------------------------------------------------------- 1 | ## 2. 核心语言的构建时性能强化 ## 2 | 3 | ### 2.1 外部模板 ### 4 | 5 | > 该段摘自维基百科C++11中文词条中《[外部模板](http://zh.wikipedia.org/zh-cn/C%2B%2B11#.E5.A4.96.E9.83.A8.E6.A8.A1.E6.9D.BF)》一节,并做少量修改。 6 | 7 | 在标准 C++ 中,只要在编译单元内遇到完整定义的模板,编译器都必须将其实例化(instantiate)。这会大大增加编译时间,特别是模板在许多编译单元内使用相同的参数实例化。 8 | 9 | C++11 引入了外部模板这一概念。C++ 已经有了强制编译器在特定位置开始实例化的语法: 10 | 11 | template class std::vector; 12 | 13 | 而 C++ 所缺乏的是阻止编译器在某个编译单元内实例化模板的能力。C++11 简单地扩充语法如下: 14 | 15 | extern template class std::vector; 16 | 17 | 这样就告诉编译器 **不要** 在该编译单元内将该模板实例化。 -------------------------------------------------------------------------------- /zh/appendix C++11 standards/C++11 Core language functionality improvements.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ewenwan/Cplusplus-Concurrency-In-Practice/8ae68b763434c6babda553d65449e9158d63e357/zh/appendix C++11 standards/C++11 Core language functionality improvements.md -------------------------------------------------------------------------------- /zh/appendix C++11 standards/C++11 Core language runtime performance enhancements.md: -------------------------------------------------------------------------------- 1 | ## 1. 核心语言的运行时性能强化 ## 2 | 3 | ### 1.1 右值引用和 move 语义 ### 4 | 5 | > 本小节主要参考了 IBM developerWorkds 上的一篇博文: 《C++11 标准新特性: 右值引用与转移语义》: [http://www.ibm.com/developerworks/cn/aix/library/1307_lisl_c11/](http://www.ibm.com/developerworks/cn/aix/library/1307_lisl_c11/),在此特向原作者表示感谢。 6 | 7 | 右值引用 (Rvalue Referene) 是 C++11 标准中引入的新特性 , 它实现了转移语义(Move Sementics)和完美转发(Perfect Forwarding)。它的主要目的有两个方面: 8 | 9 | - 消除两个对象交互时不必要的对象拷贝,节省运算存储资源,提高效率 10 | - 能够更简洁明确地定义泛型函数。 11 | 12 | C++( 包括 C) 中所有的表达式和变量要么是左值,要么是右值。通俗的左值的定义就是非临时对象,那些可以在多条语句中使用的对象。所有的变量都满足这个定义,在多条代码中都可以使用,都是左值。右值是指临时的对象,它们只在当前的语句中有效。 13 | 14 | 在 C++11 之前,右值是不能被引用的,最大限度就是利用常量引用来绑定一个右值,如: 15 | 16 | const int &a = 1; 17 | 18 | 在这种情况下,右值不能被修改的。但是实际上右值是可以被修改的,如: 19 | 20 | T().set().get(); 21 | 22 | T 是一个类,set 是一个函数为 T 中的一个变量赋值,get 用来取出这个变量的值。在这句中,T() 生成一个临时对象,就是右值,set() 修改了变量的值,也就修改了这个右值。 23 | 24 | 既然右值可以被修改,那么就可以实现右值引用。右值引用能够方便地解决实际工程中的问题,实现非常有吸引力的解决方案。 25 | 26 | 左值的声明符号为 ”&”, 为了和左值区分,右值的声明符号为 ”&&”。 27 | 28 | 如果临时对象通过一个接受右值的函数传递给另一个函数时,就会变成左值,因为这个临时对象在传递过程中,变成了命名对象。如下示例程序: 29 | 30 | void process_value(int& i) { 31 | std::cout << "LValue processed: " << i << std::endl; 32 | } 33 | 34 | void process_value(int&& i) { 35 | std::cout << "RValue processed: " << i << std::endl; 36 | } 37 | 38 | void forward_value(int&& i) { 39 | process_value(i); 40 | } 41 | 42 | int main() { 43 | int a = 0; 44 | process_value(a); 45 | process_value(1); 46 | forward_value(2); 47 | } 48 | 49 | 虽然 2 这个立即数在函数 forward_value 接收时是右值,但到了 process_value 接收时,变成了左值。 50 | 51 | C++03 性能上被长期被诟病的其中之一,就是其耗时且不必要的深度拷贝。深度拷贝会发生在当对象是以传值的方式传递。举例而言,`std::vector` 是内部保存了 C-style 数组的一个包装,如果一个`std::vector` 的临时对象被建构或是从函数返回,要将其存储只能通过生成新的 `std::vector` 并且把该临时对象所有的数据复制进去。该临时对象和其拥有的内存会被摧毁。(为了讨论上的方便,这里忽略返回值优化) 52 | 53 | 在 C++11,一个 `std::vector` 的 "move 构造函数" 对某个 `vector` 的右值引用可以单纯地从右值复制其内部 C-style 数组的指针到新的 `std::vector`,然后留下空的右值。这个操作不需要数组的复制,而且空的临时对象的析构也不会摧毁内存。传回 `std::vector` 临时对象的函数不需要显式地传回`std::vector&&`。如果 `std::vector` 没有 move 构造函数,那么复制构造函数将被调用,以 `const std::vector &` 的正常形式。 如果它确实有 move 构造函数,那么就会调用 move 构造函数,能够提高程序效率。 54 | 55 | 右值引用是用来支持转移语义的。转移语义可以将资源(堆,系统对象等)从一个对象转移到另一个对象,这样能够减少不必要的临时对象的创建、拷贝以及销毁,能够大幅度提高 C++ 程序的性能。临时对象的维护(创建和销毁)对性能有严重影响。 56 | 57 | 转移语义是和拷贝语义相对的,可以类比文件的剪切与拷贝,当我们将文件从一个目录拷贝到另一个目录时,速度比剪切慢很多。 58 | 59 | 通过转移语义,临时对象中的资源能够转移其它的对象里。 60 | 61 | 在现有的 C++ 机制中,我们可以定义拷贝构造函数和赋值函数。要实现转移语义,需要定义转移构造函数,还可以定义转移赋值操作符。对于右值的拷贝和赋值会调用转移构造函数和转移赋值操作符。如果转移构造函数和转移拷贝操作符没有定义,那么就遵循现有的机制,拷贝构造函数和赋值操作符会被调用。 62 | 63 | 普通的函数和操作符也可以利用右值引用操作符实现转移语义。 64 | 65 | 以一个简单的 string 类为示例,实现拷贝构造函数和拷贝赋值操作符。 66 | 67 | class MyString { 68 | private: 69 | char* _data; 70 | size_t _len; 71 | void _init_data(const char *s) { 72 | _data = new char[_len+1]; 73 | memcpy(_data, s, _len); 74 | _data[_len] = '\0'; 75 | } 76 | 77 | public: 78 | MyString() { 79 | _data = NULL; 80 | _len = 0; 81 | } 82 | 83 | MyString(const char* p) { 84 | _len = strlen (p); 85 | _init_data(p); 86 | } 87 | 88 | MyString(const MyString& str) { 89 | _len = str._len; 90 | _init_data(str._data); 91 | std::cout << "Copy Constructor is called! source: " << str._data << std::endl; 92 | } 93 | 94 | MyString& operator=(const MyString& str) { 95 | if (this != &str) { 96 | _len = str._len; 97 | _init_data(str._data); 98 | } 99 | std::cout << "Copy Assignment is called! source: " << str._data << std::endl; 100 | return *this; 101 | } 102 | 103 | virtual ~MyString() { 104 | if (_data) free(_data); 105 | } 106 | }; 107 | 108 | int main() { 109 | MyString a; 110 | a = MyString("Hello"); 111 | std::vector vec; 112 | vec.push_back(MyString("World")); 113 | } 114 | 115 | 运行结果 : 116 | 117 | Copy Assignment is called! source: Hello 118 | Copy Constructor is called! source: World 119 | 120 | 这个 string 类已经基本满足我们演示的需要。在 main 函数中,实现了调用拷贝构造函数的操作和拷贝赋值操作符的操作。MyString(“Hello”) 和 MyString(“World”) 都是临时对象,也就是右值。虽然它们是临时的,但程序仍然调用了拷贝构造和拷贝赋值,造成了没有意义的资源申请和释放的操作。如果能够直接使用临时对象已经申请的资源,既能节省资源,有能节省资源申请和释放的时间。这正是定义转移语义的目的。 121 | 122 | 我们先定义转移构造函数。 123 | 124 | MyString(MyString&& str) { 125 | std::cout << "Move Constructor is called! source: " << str._data << std::endl; 126 | _len = str._len; 127 | _data = str._data; 128 | str._len = 0; 129 | str._data = NULL; 130 | } 131 | 132 | 和拷贝构造函数类似,需要注意几点: 133 | 134 | 1. 参数(右值)的符号必须是右值引用符号,即“&&”。 135 | 2. 参数(右值)不可以是常量,因为我们需要修改右值。 136 | 3. 参数(右值)的资源链接和标记必须修改。否则,右值的析构函数就会释放资源。转移到新对象的资源也就无效了。 137 | 138 | 现在我们定义转移赋值操作符。 139 | 140 | MyString& operator=(MyString&& str) { 141 | std::cout << "Move Assignment is called! source: " << str._data << std::endl; 142 | if (this != &str) { 143 | _len = str._len; 144 | _data = str._data; 145 | str._len = 0; 146 | str._data = NULL; 147 | } 148 | return *this; 149 | } 150 | 151 | 由此看出,编译器区分了左值和右值,对右值调用了转移构造函数和转移赋值操作符。节省了资源,提高了程序运行的效率。 152 | 153 | 有了右值引用和转移语义,我们在设计和实现类时,对于需要动态申请大量资源的类,应该设计转移构造函数和转移赋值函数,以提高应用程序的效率。 154 | 155 | 既然编译器只对右值引用才能调用转移构造函数和转移赋值函数,而所有命名对象都只能是左值引用,如果已知一个命名对象不再被使用而想对它调用转移构造函数和转移赋值函数,也就是把一个左值引用当做右值引用来使用,怎么做呢?标准库提供了函数 std::move,这个函数以非常简单的方式将左值引用转换为右值引用。 156 | 157 | 基于安全的理由,具名的参数将永远不被认定为右值,即使它是被如此声明的;为了获得右值必须使用 `std::move()`。 158 | 159 | 完美转发适用于这样的场景:需要将一组参数原封不动的传递给另一个函数。 160 | 161 | “原封不动”不仅仅是参数的值不变,在 C++ 中,除了参数值之外,还有一下两组属性: 162 | 163 | 左值/右值和 const/non-const。完美转发就是在参数传递过程中,所有这些属性和参数值都不能改变。在泛型函数中这样的需求非常普遍。 164 | 165 | ### 1.2 泛化的常量表达式 constexpr ### 166 | 167 | C++ 本来就已具备常量表示式(constant expression)的概念。C++11引进关键字 constexpr 允许用户保证函数或是对象建构式是编译期常量。 168 | 169 | constexpr 可以修饰变量,函数,和类的构造函数。 170 | 171 | 当 constexpr 修饰变量时,应满足: 172 | 173 | - 如果该变量是某个类的对象,则它应该立即被构造,如果是基本类型,则它应该被立即赋值。 174 | - 构造函数的参数或者赋给该变量的值必须是字面常量,constexpr 变量或 constexpr 函数。 175 | - 如果是使用构造函数创建该变量(无论显式构造或者隐式构造),构造函数必须满足 constexpr 特性。 176 | 177 | 当 constexpr 修饰函数时,应满足: 178 | 179 | - 该函数不能是虚函数。 180 | - return 返回值应该是字面类型的常量。 181 | - 该函数每个参数必须是字面类型常量。 182 | - 函数体只能包含: 183 | 1. null 语句。 184 | 2. [[../static_assert|static_assert]] 语句。 185 | 3. typedef 声明或 模板别名声明(但该模板别名声明不定义类类型与枚举类型)。 186 | 4. using 声明。 187 | 5. using 指导语句。 188 | 6. 只能存在唯一的 return 语句,并且 return 语句只能包含字面常量,constexpr 变量或 constexpr 函数。 189 | 190 | 当 constexpr 修饰类构造函数时,应满足: 191 | 192 | - 构造函数的每个参数必须是字面类型常量。 193 | - 该类不能有虚基类。 194 | - 构造函数体必须满足以下条件: 195 | 196 | 1. null 语句。 197 | 2. [[../static_assert|static_assert]] 语句。 198 | 3. typedef 声明或 模板别名声明(但该模板别名声明不定义类类型与枚举类型)。 199 | 4. using 声明。 200 | 5. using 指导语句。 201 | 202 | - 构造函数中不能有 try-语句块。 203 | - 该类的每个基类和非静态成员变量必须是初始化。 204 | - 每一个隐式转换必须是常量表达式。 205 | 206 | 请看下例([参考](http://en.cppreference.com/w/cpp/language/constexpr)): 207 | 208 | #include 209 | #include 210 | 211 | // constexpr functions use recursion rather than iteration 212 | constexpr int factorial(int n) 213 | { 214 | return n <= 1 ? 1 : (n * factorial(n-1)); 215 | } 216 | 217 | // literal class 218 | class conststr { 219 | const char * p; 220 | std::size_t sz; 221 | public: 222 | template 223 | constexpr conststr(const char(&a)[N]) : p(a), sz(N-1) {} 224 | // constexpr functions signal errors by throwing exceptions from operator ?: 225 | constexpr char operator[](std::size_t n) const { 226 | return n < sz ? p[n] : throw std::out_of_range(""); 227 | } 228 | constexpr std::size_t size() const { return sz; } 229 | }; 230 | 231 | constexpr std::size_t countlower(conststr s, std::size_t n = 0, 232 | std::size_t c = 0) { 233 | return n == s.size() ? c : 234 | s[n] >= 'a' && s[n] <= 'z' ? countlower(s, n+1, c+1) : 235 | countlower(s, n+1, c); 236 | } 237 | 238 | // output function that requires a compile-time constant, for testing 239 | template struct constN { 240 | constN() { std::cout << n << '\n'; } 241 | }; 242 | 243 | int main() 244 | { 245 | std::cout << "4! = " ; 246 | constN out1; // computed at compile time 247 | 248 | volatile int k = 8; // disallow optimization using volatile 249 | std::cout << k << "! = " << factorial(k) << '\n'; // computed at run time 250 | 251 | std::cout << "Number of lowercase letters in \"Hello, world!\" is "; 252 | constN out2; // implicitly converted to conststr 253 | } 254 | 255 | 256 | ### 1.3 对 POD 类型定义的修正 ### 257 | 258 | > 本小节主要来源于[维基百科C++11](http://zh.wikipedia.org/zh-cn/C%2B%2B11)词条中的《[对POD定义的修正](http://zh.wikipedia.org/zh-cn/C%2B%2B11#.E5.B0.8DPOD.E5.AE.9A.E7.BE.A9.E7.9A.84.E4.BF.AE.E6.AD.A3)》: 259 | 260 | 在标准C++,一个结构(struct)为了能够被当成 POD,必须遵守几条规则。有很好的理由使我们想让大量的类型符合这种定义,符合这种定义的类型能够允许产生与C兼容的对象布局(object layout)。然而,C++03 的规则太严苛了。 261 | 262 | C++11 将会放宽关于 POD 的定义。 263 | 264 | 当 class/struct 是极简的(`trivial`)、属于标准布局(standard-layout),以及他的所有非静态(non-static)成员都是 POD 时,会被视为 POD。 265 | 266 | 一个极简的类型或结构符合以下定义: 267 | 268 | - 极简的默认建构式。这可以使用默认建构式语法,例如 `SomeConstructor() = default;` 269 | - 极简的复制建构式,可使用默认语法(default syntax) 270 | - 极简的赋值运算符,可使用默认语法(default syntax) 271 | - 极简的析构式,不可以是虚拟的(virtual) 272 | 273 | 一个标准布局(standard-layout)的类型或结构符合以下定义: 274 | 275 | - 只有非静态的(non-static)数据成员,且这些成员也是符合标准布局的类型 276 | - 对所有non-static成员有相同的访问控制(public, private, protected) 277 | - 没有虚函数 278 | - 没有虚拟基类 279 | - 只有符合标准布局的基类 280 | - 没有和第一个定义的non-static成员相同类型的基类 281 | - 若非没有带有non-static成员的基类,就是最底层(继承最末位)的类型没有non-static数据成员而且至多一个带有non-static成员的基类。基本上,在该类型的继承体系中只会有一个类型带有non-static成员。 282 | -------------------------------------------------------------------------------- /zh/appendix C++11 standards/README.md: -------------------------------------------------------------------------------- 1 | # 本章导读 # 2 | 3 | 本章主要介绍 C++11 标准的一些新特性,本文的素材主要来源于: 4 | 5 | Bjarne Stroustrup 个人主页: 6 | 7 | - C++11 - the new ISO C++ standard: [http://www.stroustrup.com/C++11FAQ.html](http://www.stroustrup.com/C++11FAQ.html) 8 | - The C++ Programming Language: [http://www.stroustrup.com/C++.html](http://www.stroustrup.com/C++.html) 9 | 10 | 11 | C++ 标准委员会: 12 | 13 | - The C++ Standards Committee: [http://www.open-std.org/jtc1/sc22/wg21/](http://www.open-std.org/jtc1/sc22/wg21/) 14 | 15 | 维基百科: 16 | 17 | - 英文 C++11: [http://en.wikipedia.org/wiki/C%2B%2B11](http://en.wikipedia.org/wiki/C%2B%2B11) 18 | - 中文 C++11: [http://zh.wikipedia.org/zh-cn/C%2B%2B11](http://zh.wikipedia.org/zh-cn/C%2B%2B11) 19 | 20 | Isocpp.org: 21 | 22 | - [http://isocpp.org](http://isocpp.org) 23 | 24 | Boostcon: 25 | 26 | - [https://github.com/boostcon](https://github.com/boostcon) 27 | 28 | 按照维基百科中的分类方法,本文将对 C++11 标准的新特性按照以下四个方面来解释,分别是: 29 | 30 | 1. 核心语言的运行时性能强化 31 | 2. 核心语言的构建时性能强化 32 | 3. 核心语言的可用性强化 33 | 4. 核心语言的功能提升 34 | 5. C++ 标准库的变更 35 | 36 | # 附录 C++11 新标准概览 # 37 | 38 | ## 1. [核心语言的运行时性能强化](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/appendix%20C%2B%2B11%20standards/C%2B%2B11%20Core%20language%20runtime%20performance%20enhancements.md) ## 39 | 40 | 本小节主要包括: 41 | 42 | 1. 右值引用和 move 语义 43 | 2. 泛化的常量表达式 constexpr 44 | 3. 对 POD 类型定义的修正。 45 | 46 | ## 2. [核心语言的构建时性能强化](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/appendix%20C%2B%2B11%20standards/C%2B%2B11%20Core%20language%20build%20time%20performance%20enhancements.md) ## 47 | 48 | 本小节主要包括: 49 | 50 | 1. 外部模板。 51 | 52 | ## 3. [核心语言的可用性强化](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/appendix%20C%2B%2B11%20standards/C%2B%2B11%20Core%20language%20usability%20enhancements.md) ## 53 | 54 | 本小节主要包括: 55 | 56 | 1. 初始化列表(`std::initializer_list`) 57 | 2. 统一的初始化方式 58 | 3. 类型推导(auto 和 decltype 关键字) 59 | 4. 基于范围的 for 循环 60 | 5. lambda 表达式 61 | 6. 另一种可选的函数语法 62 | 7. 对象创建优化 63 | 8. 显式虚函数重载 64 | 9. 空指针常量(nullptr) 65 | 10. 强类型枚举 66 | 11. 右尖括号(>) 67 | 12. 显式类型转换操作符 68 | 13. 模板别名 69 | 14. 无限制 union。 70 | 71 | ## 4. 核心语言的功能提升 ## 72 | 73 | 本小节主要包括: 74 | 75 | 1. 变长参数模板 76 | 2. 新的字符串字面值 77 | 3. 用户自定义的字面值 78 | 4. 多线程内存模型 79 | 5. 线程本地存储 80 | 6. 显式地使用或禁用某些特殊成员函数(构造函数,拷贝构造,赋值操作符,析构等) 81 | 7. long long int类型 82 | 8. 静态断言 assertions 83 | 9. 允许 sizeof 运算符作用在类型的数据成员上,无须明确的对象 84 | 10. 垃圾回收机制 85 | 11. 属性 86 | 87 | ## 5. C++ 标准库的变更 ## 88 | 89 | 本小节主要包括: 90 | 91 | 1. 标准库组件上的升级 92 | 2. 多线程支持 93 | 3. 元组(tuple)类型 94 | 4. 散列表(hash table) 95 | 5. 正则表达式 96 | 6. 通用智能指针 97 | 7. 可扩展的随机数功能 98 | 8. 包装引用 99 | 9. 多态函数对象包装器 100 | 10. 用于元编程的类型属性 101 | 11. 用于计算函数对象返回类型的统一方法 102 | -------------------------------------------------------------------------------- /zh/appendix C++11 standards/web-resources.md: -------------------------------------------------------------------------------- 1 | Bjarne Stroustrup 个人主页: 2 | 3 | - C++11 - the new ISO C++ standard: [http://www.stroustrup.com/C++11FAQ.html](http://www.stroustrup.com/C++11FAQ.html) 4 | - The C++ Programming Language: [http://www.stroustrup.com/C++.html](http://www.stroustrup.com/C++.html) 5 | - The C++ Standards Committee: [http://www.open-std.org/jtc1/sc22/wg21/](http://www.open-std.org/jtc1/sc22/wg21/) 6 | - 维基百科英文 C++11: [http://en.wikipedia.org/wiki/C%2B%2B11](http://en.wikipedia.org/wiki/C%2B%2B11) 7 | - 维基百科中文 C++11: [http://zh.wikipedia.org/zh-cn/C%2B%2B11](http://zh.wikipedia.org/zh-cn/C%2B%2B11) 8 | - [http://isocpp.org](http://isocpp.org) 9 | - [https://github.com/boostcon](https://github.com/boostcon) 10 | - C++11 标准新特性: 右值引用与转移语义: [http://www.ibm.com/developerworks/cn/aix/library/1307_lisl_c11/](http://www.ibm.com/developerworks/cn/aix/library/1307_lisl_c11/) 11 | - Summary of C++11 Feature Availability in gcc and MSVC: [http://www.aristeia.com/C++11/C++11FeatureAvailability.htm](http://www.aristeia.com/C++11/C++11FeatureAvailability.htm) 12 | -------------------------------------------------------------------------------- /zh/chapter1-Introduction/1.1 What is concurrency.md: -------------------------------------------------------------------------------- 1 | ## 1.1 什么是并发 ## 2 | 3 | 并发(Concurrency)在我们的现实世界中随处可见,以至于我们常常忽略了它的存在,比方说你在工作(假设你是一名程序员,你的工作就是编程)的时候也可以听听自己喜欢的音乐,并且你的耳朵并不会因为手头的工作而忽略了声音的存在(当然,除非你自己有意的去忽略它,但你还是能够听得见声音,只是你的大脑可能不会去感受音乐的节奏),此时你的大脑既要控制你的双手敲击键盘,也要控制你的耳朵去感受音乐。因此,在一定程度上,你的大脑就在并发地处理不同的事情,并且每个时刻都可能会侧重处理某件事情,比如某个时刻音乐达到高潮并且是你喜欢的旋律,你可能会放慢或者停止手边的工作,但在另外一个时刻你正在编写关键代码,需要全神贯注来避免 Bug 的出现,你可能会把声音调小一点或者干脆摘掉耳机。所以,我们的大脑就在并发地指导我们完成各种任务,或者换一种说法,我们需要处理的任务并发地征用我们的大脑,大脑就相当于计算机的 CPU,而待处理的任务就相当于计算机程序(更确切地说应该是进程或线程等执行实体)。 4 | 5 | 不过在现实世界中,我们并不会严格定义什么是并发。而在计算机程序世界中,为了编写高性能的代码,我们应该理解什么是并发,并发的基本特性是什么,哪些问题可以使用并发编程来(高效地)解决,哪些情况下又应该尽量避免使用并发编程,我们在使用并发编程时需要注意一些什么问题,本章的将会给大家介绍并发的基本概念,带领大家学习并发编程的基本技巧。 6 | 7 | 8 | ## 1.2 并发与并行的联系和区别 ## 9 | 10 | 与并发相近的另一个概念是并行(Parallel)。和并发所描述的情况一样,并行也是指两个或多个任务被同时执行。但是严格来讲,并发和并行的概念并是不等同的,两者存在很大的差别。下面我们来看看计算机科学家们是怎么区分并发和并行的。 11 | 12 | ### 1.2.1 Erlang 之父 Joe Armstrong 的观点 ### 13 | 14 | Erlang 是一种通用的并行程序设计语言,在并行、分布式和容错等方面表现优异。下面是 Erlang 官方的介绍: 15 | 16 | > Erlang is a programming language used to build massively scalable soft real-time systems with requirements on high availability. Erlang's runtime system has built-in support for concurrency, distribution and fault tolerance. 17 | 18 | Erlang 的发明者 Joe Armstrong 在他的一篇博文([原文链接](http://joearms.github.io/2013/04/05/concurrent-and-parallel-programming.html))中提到如何向一个 5 岁的小孩去介绍并发和并行的区别,并给出了下面一幅图(下图是自己重绘的,[原图连接](http://joearms.github.io/2013/04/05/concurrent-and-parallel-programming.html)): 19 | 20 | ![并发与并行的区别](https://raw.github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/master/images/chapter1/concurrent-vs-parallel.png) 21 | 22 | 直观来讲,并发是两个等待队列中的人同时去竞争一台咖啡机(当然,人是有理性懂礼貌的动物(也不排除某些很霸道的人插队的可能),两队列中的排队者也可能约定交替使用咖啡机,也可能是大家同时竞争咖啡机,谁先竞争到咖啡机谁使用,不过后一种的方法可能引发冲突,因为两个队列里面排在队列首位的人可能同时使用咖啡机),每个等待者在使用咖啡机之前不仅需要知道排在他前面那个人是否已经使用完了咖啡机,还需知道另一个队列中排在首位的人是否也正准备使用咖啡机;而并行是每个队列拥有自己的咖啡机,两个队列之间并没有竞争的关系,队列中的某个排队者只需等待队列前面的人使用完咖啡机,然后再轮到自己使用咖啡机。 23 | 24 | 因此,并发意味着多个执行实体(比方说上面例子中的人)可能需要竞争资源(咖啡机),因此就不可避免带来竞争和同步的问题;而并行则是不同的执行实体拥有各自的资源,相互之间可能互不干扰。 25 | 26 | 27 | ### 1.2.2. Go 发明者之一 Rob Pike 的观点 ### 28 | 29 | Go 是一门新兴的编程语言,Go 官方对其介绍如下: 30 | 31 | > The Go programming language is an open source project to make programmers more productive. 32 | > 33 | > Go is expressive, concise, clean, and efficient. Its concurrency mechanisms make it easy to write programs that get the most out of multicore and networked machines. 34 | 35 | Go 的并行机制使其非常容易编写多核和网络应用。Go 语言的并发模型基于 CSP(Communicating sequential processes, 参见维基百科 [CSP](http://en.wikipedia.org/wiki/Communicating_Sequential_Processes))。Go 提供了 goroutines(并发执行), channels(同步和通信), select(多路并发控制) 等特性来支持并发编程。Go 的发明者之一 Rob Pick 在他的一篇讲稿([Concurrency is not Parallelism(it's better)](http://concur.rspace.googlecode.com/hg/talk/concur.html))中提到: 36 | 37 | > [Concurrency](http://concur.rspace.googlecode.com/hg/talk/concur.html#slide-3): Programming as the composition of independently executing processes. 38 | > 39 | > [Parallelism](http://concur.rspace.googlecode.com/hg/talk/concur.html#slide-4): Programming as the simultaneous execution of (possibly related) computations. 40 | 41 | Rob 认为并发是程序本身的一种特性,程序被分为多个可独立执行的部分,而各个可独立执行的片段通过通信手段进行协调(后文会提到),而并行则是程序的计算过程(不同的计算过程可能相关联)同时执行。 42 | 43 | Rob Pike 的观点是: 并发是一次处理(dealing with)很多事情,而并行是一次做(doing)很多事情.(注: 英文词汇的表达也很微妙)[原文](http://concur.rspace.googlecode.com/hg/talk/concur.html#slide-5)是如下: 44 | 45 | > Concurrency is about dealing with lots of things at once. 46 | > 47 | > Parallelism is about doing lots of things at once. 48 | 49 | 前者是关于程序结构的,而后者是关于程序执行的。Rob 认为: 50 | 51 | > Concurrency provides a way to structure a solution to solve a problem that may (but not necessarily) be parallelizable. 52 | 53 | 即我们可以利用并发的手段去构建一种解决方案来解决那些有可能被并行处理的问题。 54 | 55 | 作者在本文中还[提到](http://concur.rspace.googlecode.com/hg/talk/concur.html#slide-7),设计并发程序时应该将程序分为多个执行片段,使得每个片段可以独立执行。不同执行片段通过通信(Communication )来进行协调。因此 Go 的并发模型基于 CSP: C. A. R. Hoare: Communicating Sequential Processes (CACM 1978) 56 | 57 | 作者后面还给出了一个例子来阐述他的观点,感兴趣的读者可以继续阅读:([Concurrency is not Parallelism(it's better)](http://concur.rspace.googlecode.com/hg/talk/concur.html)) 58 | 59 | ### 1.2.3 Haskell 语言中的并发与并行### 60 | 61 | (本小节暂未完成) 62 | 63 | ### 1.2.4 其他观点 ### 64 | 65 | 另外,Intel 中文网站的一篇文章([原文链接](http://software.intel.com/zh-cn/blogs/2010/11/30/400006465))曾这样写道(可能不是很权威,不过可以大致说明并发与并行的区别), 66 | 67 | 并发(Concurrence):指两个或两个以上的事件或活动在同一时间间隔内发生。并发的实质是单个物理 CPU(也可以多个物理CPU) 在若干道程序之间多路复用,并发可以对有限物理资源强制行使多用户共享以提高效率,如下图所示: 68 | 69 | ![并发](https://raw.github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/master/images/chapter1/intel-blog-concurrency.png) 70 | 71 | 并行(Parallelism)指两个或两个以上事件或活动在同一时刻发生。在多道程序环境下,并行性使多个程序同一时刻可在不同CPU上同时执行,如下图所示: 72 | 73 | ![并行](https://raw.github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/master/images/chapter1/intel-blog-parallel.png) 74 | 75 | 因此,该文认为并发与并行的区别是:并发是一个处理器同时处理多个任务,而并行多个处理器或者是多核的处理器同时处理多个不同的任务。前者是逻辑上的同时发生(simultaneous),而后者是物理上的同时发生。 76 | 77 | 而两者的联系是:并行的事件或活动一定是并发的,但反之并发的事件或活动未必是并行的。并行性是并发性的特例,而并发性是并行性的扩展(个人不赞同此观点)。 78 | 79 | 80 | ### 1.2.5 小节 ### 81 | 82 | 本文主要讲了什么是并发以及并发和并行的联系和区别。总得来说,Joe Armstrong 的观点通俗易懂,Rob Pike 有关并发和并行的的观点也很有意思。而关于并发和并行具体的差异,本文最后介绍了一种教科书式的解释。读者可以根据自己的理解来选择认同上述某一种或几种观点。 83 | 84 | 85 | -------------------------------------------------------------------------------- /zh/chapter1-Introduction/Cplusplus-Concurrency-Introduction.md: -------------------------------------------------------------------------------- 1 | # 引言 # 2 | C++11 是 2011 年 9 月 1 号发布的。C++11 在 C++03 的基础上做了大量的改进,引入了很多新的特性,比如 Lambda 表达式,右值引用,统一的列表初始化方式,正则表达式等等。当然,其中最令人激动的特性是新标准引入了原子操作类和线程支持库。C++ 一直在语言层面缺少对多线程的支持,因此 C++11 新标准基本上弥补了这一缺陷。可以毫不夸张地说,C++11 相当于一门新的编程语言。 3 | 4 | 相信 Linux 程序员都用过 Pthread, 但有了 C++11 的 std::thread 以后,你可以在语言层面编写多线程程序了,直接的好处就是多线程程序的可移植性得到了很大的提高,所以作为一名 C++ 程序员,熟悉 C++11 的多线程编程方式还是很有益处的。 5 | 6 | 如果你对 C++11 不太熟悉,建议先看看维基百科上关于 C++11 新特性的介绍,中文C++11介绍,英文C++11介绍 ,另外C++之父 Bjarne Stroustrup 的关于 C++11 的 FAQ 也是必看的,我也收集了一些关于C++11的资料,供大家查阅,详见本章的 [资料汇](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter1-Introduction/web-resources.md) 7 | 8 | # 与 C++11 多线程相关的头文件 # 9 | 10 | C++11 新标准中引入了五个头文件来支持多线程编程,它们分别是 `, , , ` 和 ``。 11 | 12 | - ``:该头文主要声明了两个类, `std::atomic` 和 `std::atomic_flag`,另外还声明了一套 C 风格的原子类型和与 C 兼容的原子操作的函数。 13 | 14 | - ``:该头文件主要声明了 `std::thread` 类,另外 `std::this_thread` 命名空间也在该头文件中。 15 | 16 | - ``:该头文件主要声明了与互斥量(Mutex)相关的类,包括 `std::mutex_*` 一系列类,`std::lock_guard`, `std::unique_lock`, 以及其他的类型和函数。 17 | 18 | - ``:该头文件主要声明了与条件变量相关的类,包括 `std::condition_variable` 和 `std::condition_variable_any`。 19 | 20 | - ``:该头文件主要声明了 `std::promise`, `std::package_task` 两个 Provider 类,以及 `std::future` 和 `std::shared_future` 两个 Future 类,另外还有一些与之相关的类型和函数,`std::async()` 函数就声明在此头文件中。 21 | 22 | # `std::thread` "Hello world" # 23 | 下面是一个最简单的使用 std::thread 类的例子 24 | 25 | #include 26 | #include 27 | 28 | #include // std::cout 29 | #include // std::thread 30 | 31 | void thread_task() { 32 | std::cout << "hello thread" << std::endl; 33 | } 34 | 35 | int main(int argc, const char *argv[]) 36 | { 37 | std::thread t(thread_task); 38 | t.join(); 39 | 40 | return EXIT_SUCCESS; 41 | } 42 | 43 | Makefile 如下: 44 | 45 | all:Thread 46 | 47 | CC=g++ 48 | CPPFLAGS=-Wall -std=c++11 -ggdb 49 | LDFLAGS=-pthread 50 | 51 | Thread:Thread.o 52 | $(CC) $(LDFLAGS) -o $@ $^ 53 | 54 | Thread.o:Thread.cc 55 | $(CC) $(CPPFLAGS) -o $@ -c $^ 56 | 57 | 58 | .PHONY: 59 | clean 60 | 61 | clean: 62 | rm Thread.o Thread 63 | 64 | 注意在 Linux GCC4.6 环境下,编译时需要加 -pthread,否则执行时会出现: 65 | 66 | $ ./Thread 67 | terminate called after throwing an instance of 'std::system_error' 68 | what(): Operation not permitted 69 | Aborted (core dumped) 70 | 71 | 原因是 GCC 默认没有加载 pthread 库,据说在后续的版本中可以不用在编译时添加 -pthread 选项。 72 | 如果 -std=c++11 不被支持,请尝试使用 -std=c++0x 代替并确保GCC已经升级。 73 | 更多的有关 C++11 Concurrency 的介绍将在后续的一系列博客中写出,希望自己勤快一点吧 ;-) 74 | -------------------------------------------------------------------------------- /zh/chapter1-Introduction/README.md: -------------------------------------------------------------------------------- 1 | # 本章导读 # 2 | 3 | 本章主要内容如下: 4 | 5 | - 什么是并发编程,并发和并行的联系和区别,为什么需要并发编程。 6 | - 并发编程应用场景,并提出几个并发编程的经典示例。 7 | - C++ 并发编程初探。 8 | 9 | 我会在本章介绍一些最基本的概念,关键词如:程序,并发,并行,进程,多线程,进程间通信,线程间数据共享问题,通过对本章内容的学习,你会对并发编程有一个大致的认识,究竟什么是并发编程,并发和并行的联系和区别,什么场景下需要使用并发编程以及并发的优势和缺陷等。对于初学者,我会给出几个经典的示例激发大家的兴趣。 10 | 11 | # 第一章 并发编程基础 # 12 | 13 | ## 1.1 [什么是并发编程](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter1-Introduction/1.1%20What%20is%20concurrency.md) ## 14 | ## 1.2 [并发与并行的区别和联系](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter1-Introduction/1.1%20What%20is%20concurrency.md#12-%E5%B9%B6%E5%8F%91%E4%B8%8E%E5%B9%B6%E8%A1%8C%E7%9A%84%E8%81%94%E7%B3%BB%E5%92%8C%E5%8C%BA%E5%88%AB) ## 15 | ## 1.3 为什么需要并发编程 ## 16 | ## 1.4 并发编程应用场景和经典示例 ## 17 | ## 1.5 [C++ 并发编程初探](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter1-Introduction/Cplusplus-Concurrency-Introduction.md) ## 18 | ## 1.6 [资料汇](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter1-Introduction/web-resources.md) ## 19 | 20 | 21 | -------------------------------------------------------------------------------- /zh/chapter1-Introduction/web-resources.md: -------------------------------------------------------------------------------- 1 | # 资料汇 # 2 | 3 | [http://www.open-std.org/jtc1/sc22/wg21/](http://www.open-std.org/jtc1/sc22/wg21/ "WG21") 4 | 5 | C++0x/C++11 Support in GCC:[http://gcc.gnu.org/projects/cxx0x.html](http://gcc.gnu.org/projects/cxx0x.html "C++0x/C++11 Support in GCC") 6 | 7 | What is C++0x:[https://www2.research.att.com/~bs/what-is-2009.pdf](https://www2.research.att.com/~bs/what-is-2009.pdf "What is C++0x") 8 | 9 | Overview of the New C++:[http://www.artima.com/shop/overview_of_the_new_cpp](http://www.artima.com/shop/overview_of_the_new_cpp "Overview of the New C++") 10 | 11 | Overview of the New C++ (C++0x).pdf:[http://ishare.iask.sina.com.cn/f/20120005.html?from=like](http://ishare.iask.sina.com.cn/f/20120005.html?from=like "Overview of the New C++ (C++0x).pdf") 12 | 13 | A Brief Look at C++0x:[http://www.artima.com/cppsource/cpp0x.html](http://www.artima.com/cppsource/cpp0x.html "A Brief Look at C++0x") 14 | 15 | Summary of C++11 Feature Availability in gcc and MSVC:[http://www.aristeia.com/C++11/C++11FeatureAvailability.htm](http://www.aristeia.com/C++11/C++11FeatureAvailability.htm "Summary of C++11 Feature Availability in gcc and MSVC") 16 | 17 | C++ 11: Come Closer:[http://www.codeproject.com/Articles/344282/Cplusplus-11-Come-Closer](http://www.codeproject.com/Articles/344282/Cplusplus-11-Come-Closer "C++ 11: Come Closer") 18 | 19 | C++11 threads, locks and condition variables: [http://www.codeproject.com/Articles/598695/Cplusplus11-threads-locks-and-condition-variables](http://www.codeproject.com/Articles/598695/Cplusplus11-threads-locks-and-condition-variables "C++11 threads, locks and condition variables") 20 | 21 | Move Semantics and Perfect Forwarding in C++11:[http://www.codeproject.com/Articles/397492/Move-Semantics-and-Perfect-Forwarding-in-Cplusplus](http://www.codeproject.com/Articles/397492/Move-Semantics-and-Perfect-Forwarding-in-Cplusplus "Move Semantics and Perfect Forwarding in C++11") 22 | 23 | [http://solarianprogrammer.com/categories/C++11/](http://solarianprogrammer.com/categories/C++11/) 24 | 25 | C++11 Concurrency:[http://www.baptiste-wicht.com/2012/03/cpp11-concurrency-part1-start-threads/](http://www.baptiste-wicht.com/2012/03/cpp11-concurrency-part1-start-threads/ "C++11 Concurrency") 26 | 27 | [http://www.hpl.hp.com/personal/Hans_Boehm/misc_slides/sfacm-cleaned.pdf](http://www.hpl.hp.com/personal/Hans_Boehm/misc_slides/sfacm-cleaned.pdf) 28 | 29 | [http://en.cppreference.com/w/cpp/thread](http://en.cppreference.com/w/cpp/thread) 30 | 31 | [http://isocpp.org/blog/2012/12/c11-a-cheat-sheet-alex-sinyakov](http://isocpp.org/blog/2012/12/c11-a-cheat-sheet-alex-sinyakov) 32 | 33 | The Biggest Changes in C++11:[http://blog.smartbear.com/c-plus-plus/the-biggest-changes-in-c11-and-why-you-should-care/](http://blog.smartbear.com/c-plus-plus/the-biggest-changes-in-c11-and-why-you-should-care/ "The Biggest Changes in C++11") 34 | 35 | Ten C++11 Features Every C++ Developer Should Use:[http://www.codeproject.com/Articles/570638/Ten-Cplusplus11-Features-Every-Cplusplus-Developer](http://www.codeproject.com/Articles/570638/Ten-Cplusplus11-Features-Every-Cplusplus-Developer "Ten C++11 Features Every C++ Developer Should Use") 36 | 37 | C++11 – A Glance [part 1 of n]:[http://www.codeproject.com/Articles/312029/Cplusplus11-A-Glance-part-1-of-n](http://www.codeproject.com/Articles/312029/Cplusplus11-A-Glance-part-1-of-n " C++11 – A Glance [part 1 of n]") 38 | 39 | C++11 – A Glance [part 2 of n]:[http://www.codeproject.com/Articles/314415/Cplusplus11-A-Glance-part-2-of-n](http://www.codeproject.com/Articles/314415/Cplusplus11-A-Glance-part-2-of-n "C++11 – A Glance [part 2 of n]") 40 | 41 | C++11(及现代C++风格)和快速迭代式开发:[http://mindhacks.cn/2012/08/27/modern-cpp-practices/](http://mindhacks.cn/2012/08/27/modern-cpp-practices/ "C++11(及现代C++风格)和快速迭代式开发") 42 | 43 | Lambda Functions in C++11 - the Definitive Guide:[http://www.cprogramming.com/c++11/c++11-lambda-closures.html](http://www.cprogramming.com/c++11/c++11-lambda-closures.html "Lambda Functions in C++11 - the Definitive Guide") 44 | 45 | Better types in C++11 - nullptr, enum classes (strongly typed enumerations) and cstdint:[http://www.cprogramming.com/c++11/c++11-nullptr-strongly-typed-enum-class.html](http://www.cprogramming.com/c++11/c++11-nullptr-strongly-typed-enum-class.html "Better types in C++11 - nullptr, enum classes (strongly typed enumerations) and cstdint") 46 | 47 | Rvalue-references-and-move-semantics-in-c++11:[http://www.cprogramming.com/c++11/rvalue-references-and-move-semantics-in-c++11.html](http://www.cprogramming.com/c++11/rvalue-references-and-move-semantics-in-c++11.html "Rvalue-references-and-move-semantics-in-c++11") 48 | 49 | [http://www.gotw.ca/publications/index.htm](http://www.gotw.ca/publications/index.htm) 50 | 51 | [http://www.devx.com/SpecialReports/Door/38865](http://www.devx.com/SpecialReports/Door/38865) 52 | 53 | Multi-threading in C++0x:[http://accu.org/index.php/journals/1584](http://accu.org/index.php/journals/1584 "Multi-threading in C++0x") 54 | 55 | C++ 0X feature summary cheat sheat:[http://www.iesensor.com/blog/2011/05/31/c-0x-feature-summary-cheat-sheat/](http://www.iesensor.com/blog/2011/05/31/c-0x-feature-summary-cheat-sheat/ "C++ 0X feature summary cheat sheat") 56 | 57 | Multithreading in C++0x part 1: Starting Threads:[http://www.justsoftwaresolutions.co.uk/threading/multithreading-in-c++0x-part-1-starting-threads.html](http://www.justsoftwaresolutions.co.uk/threading/multithreading-in-c++0x-part-1-starting-threads.html "Multithreading in C++0x part 1") 58 | 59 | [http://en.cppreference.com/w/cpp/thread](http://en.cppreference.com/w/cpp/thread) 60 | 61 | [http://www.cplusplus.com/reference/multithreading/](http://www.cplusplus.com/reference/multithreading/) 62 | 63 | Parallelism is not concurrency: [http://existentialtype.wordpress.com/2011/03/17/parallelism-is-not-concurrency/](http://existentialtype.wordpress.com/2011/03/17/parallelism-is-not-concurrency/) 64 | 65 | Concurrent and Parallel Programming: [http://joearms.github.io/2013/04/05/concurrent-and-parallel-programming.html](http://joearms.github.io/2013/04/05/concurrent-and-parallel-programming.html) 66 | 67 | Parallelism /= Concurrency: [http://ghcmutterings.wordpress.com/2009/10/06/parallelism-concurrency/](http://ghcmutterings.wordpress.com/2009/10/06/parallelism-concurrency/) 68 | 69 | Parallelism vs. Concurrency: [http://www.haskell.org/haskellwiki/Parallelism_vs._Concurrency](http://www.haskell.org/haskellwiki/Parallelism_vs._Concurrency) 70 | 71 | Concurrency is not Parallelism: [http://concur.rspace.googlecode.com/hg/talk/concur.html#title-slide](http://concur.rspace.googlecode.com/hg/talk/concur.html#title-slide) 72 | 73 | Concurrency vs Parallelism - What is the difference?: [http://stackoverflow.com/questions/1050222/concurrency-vs-parallelism-what-is-the-difference](http://stackoverflow.com/questions/1050222/concurrency-vs-parallelism-what-is-the-difference) 74 | 75 | Difference between concurrent programming and parallel programming: [http://stackoverflow.com/questions/1897993/difference-between-concurrent-programming-and-parallel-programming?rq=1](http://stackoverflow.com/questions/1897993/difference-between-concurrent-programming-and-parallel-programming?rq=1) 76 | 77 | Parallelism and concurrency need different tools: [http://www.yosefk.com/blog/parallelism-and-concurrency-need-different-tools.html](http://www.yosefk.com/blog/parallelism-and-concurrency-need-different-tools.html) -------------------------------------------------------------------------------- /zh/chapter10-Concurrent-Data-Structure/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ewenwan/Cplusplus-Concurrency-In-Practice/8ae68b763434c6babda553d65449e9158d63e357/zh/chapter10-Concurrent-Data-Structure/README.md -------------------------------------------------------------------------------- /zh/chapter10-Concurrent-Data-Structure/web-resources.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ewenwan/Cplusplus-Concurrency-In-Practice/8ae68b763434c6babda553d65449e9158d63e357/zh/chapter10-Concurrent-Data-Structure/web-resources.md -------------------------------------------------------------------------------- /zh/chapter11-Application/web-resources.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ewenwan/Cplusplus-Concurrency-In-Practice/8ae68b763434c6babda553d65449e9158d63e357/zh/chapter11-Application/web-resources.md -------------------------------------------------------------------------------- /zh/chapter2-Thread-Libraries/2.1 Pthread.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ewenwan/Cplusplus-Concurrency-In-Practice/8ae68b763434c6babda553d65449e9158d63e357/zh/chapter2-Thread-Libraries/2.1 Pthread.md -------------------------------------------------------------------------------- /zh/chapter2-Thread-Libraries/2.2 Windows multithreading.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ewenwan/Cplusplus-Concurrency-In-Practice/8ae68b763434c6babda553d65449e9158d63e357/zh/chapter2-Thread-Libraries/2.2 Windows multithreading.md -------------------------------------------------------------------------------- /zh/chapter2-Thread-Libraries/2.3 Multithreading-libraries-comparison.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ewenwan/Cplusplus-Concurrency-In-Practice/8ae68b763434c6babda553d65449e9158d63e357/zh/chapter2-Thread-Libraries/2.3 Multithreading-libraries-comparison.md -------------------------------------------------------------------------------- /zh/chapter2-Thread-Libraries/web-resources.md: -------------------------------------------------------------------------------- 1 | - [http://www.sourceware.org/pthreads-win32/](http://www.sourceware.org/pthreads-win32/) 2 | - [http://pubs.opengroup.org/onlinepubs/7908799/xsh/pthread.h.html](http://pubs.opengroup.org/onlinepubs/7908799/xsh/pthread.h.html) 3 | - POSIX Threads Programming: [https://computing.llnl.gov/tutorials/pthreads/](https://computing.llnl.gov/tutorials/pthreads/) 4 | - Multi-Threaded Programming With POSIX Threads: [http://www.cs.kent.edu/~ruttan/sysprog/lectures/multi-thread/multi-thread.html](http://www.cs.kent.edu/~ruttan/sysprog/lectures/multi-thread/multi-thread.html) 5 | - POSIX Threads Tutorial: [http://math.arizona.edu/~swig/documentation/pthreads/#pthreads](http://math.arizona.edu/~swig/documentation/pthreads/#pthreads) 6 | - Multithreaded Programming (POSIX pthreads Tutorial): [http://randu.org/tutorials/threads/](http://randu.org/tutorials/threads/) 7 | - [http://www.yolinux.com/TUTORIALS/LinuxTutorialPosixThreads.html](http://www.yolinux.com/TUTORIALS/LinuxTutorialPosixThreads.html) 8 | - [http://www.bogotobogo.com/cplusplus/multithreading_pthread.php](http://www.bogotobogo.com/cplusplus/multithreading_pthread.php) 9 | - [http://pages.cs.wisc.edu/~travitch/pthreads_primer.html](http://pages.cs.wisc.edu/~travitch/pthreads_primer.html) -------------------------------------------------------------------------------- /zh/chapter3-Thread/Introduction-to-Thread.md: -------------------------------------------------------------------------------- 1 | 本节将详细介绍 `std::thread` 的用法。 2 | 3 | `std::thread` 在 `` 头文件中声明,因此使用 `std::thread` 需包含 `` 头文件。 4 | 5 | ## `` 头文件摘要 ## 6 | 7 | `` 头文件声明了 std::thread 线程类及 `std::swap` (交换两个线程对象)辅助函数。另外命名空间 `std::this_thread` 也声明在 `` 头文件中。下面是 C++11 标准所定义的 `` 头文件摘要: 8 | 9 | >参见 N3242=11-0012 草案第 30.3 节 Threads(p1133)。 10 | 11 | namespace std { 12 | #define __STDCPP_THREADS__ __cplusplus 13 | class thread; 14 | void swap(thread& x, thread& y); 15 | namespace this_thread { 16 | thread::id get_id(); 17 | void yield(); 18 | 19 | template 20 | void sleep_until(const chrono::time_point& abs_time); 21 | 22 | template 23 | void sleep_for(const chrono::duration& rel_time); 24 | } 25 | } 26 | 27 | `` 头文件主要声明了 `std::thread` 类,另外在 `std::this_thread` 命名空间中声明了 `get_id`,`yield`,`sleep_until` 以及 `sleep_for` 等辅助函数,本章稍微会详细介绍 `std::thread` 类及相关函数。 28 | 29 | 30 | ### `std::thread` 类摘要 ### 31 | 32 | `std::thread` 代表了一个线程对象,C++11 标准声明如下: 33 | 34 | namespace std { 35 | class thread { 36 | public: 37 | // 类型声明: 38 | class id; 39 | typedef implementation-defined native_handle_type; 40 | 41 | // 构造函数、拷贝构造函数和析构函数声明: 42 | thread() noexcept; 43 | template explicit thread(F&& f, Args&&... args); 44 | ~thread(); 45 | thread(const thread&) = delete; 46 | thread(thread&&) noexcept; 47 | thread& operator=(const thread&) = delete; 48 | thread& operator=(thread&&) noexcept; 49 | 50 | // 成员函数声明: 51 | void swap(thread&) noexcept; 52 | bool joinable() const noexcept; 53 | void join(); 54 | void detach(); 55 | id get_id() const noexcept; 56 | native_handle_type native_handle(); 57 | 58 | // 静态成员函数声明: 59 | static unsigned hardware_concurrency() noexcept; 60 | }; 61 | } 62 | 63 | `std::thread` 中主要声明三类函数:(1). 构造函数、拷贝构造函数及析构函数;(2). 成员函数;(3). 静态成员函数。另外, `std::thread::id` 表示线程 ID,同时 C++11 声明如下: 64 | 65 | namespace std { 66 | class thread::id { 67 | public: 68 | id() noexcept; 69 | }; 70 | 71 | bool operator==(thread::id x, thread::id y) noexcept; 72 | bool operator!=(thread::id x, thread::id y) noexcept; 73 | bool operator<(thread::id x, thread::id y) noexcept; 74 | bool operator<=(thread::id x, thread::id y) noexcept; 75 | bool operator>(thread::id x, thread::id y) noexcept; 76 | bool operator>=(thread::id x, thread::id y) noexcept; 77 | 78 | template 79 | basic_ostream& 80 | operator<< (basic_ostream& out, thread::id id); 81 | 82 | 83 | // Hash 支持 84 | template struct hash; 85 | template <> struct hash; 86 | } 87 | 88 | ## `std::thread` 详解 ## 89 | 90 | ### `std::thread` 构造和赋值 ### 91 | 92 | #### `std::thread` 构造函数 #### 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 104 | 105 | 106 | 109 | 110 | 111 | 114 | 115 | 116 |
默认构造函数 (1)thread() noexcept;
初始化构造函数 (2) 101 | template <class Fn, class... Args>
102 | explicit thread(Fn&& fn, Args&&... args); 103 |
拷贝构造函数 [deleted] (3) 107 | thread(const thread&) = delete; 108 |
Move 构造函数 (4) 112 | thread(thread&& x) noexcept; 113 |
117 | 118 | 1. 默认构造函数(1),创建一个空的 `std::thread` 执行对象。 119 | 2. 初始化构造函数(2),创建一个 `std::thread` 对象,该 `std::thread` 对象可被 `joinable`,新产生的线程会调用 `fn` 函数,该函数的参数由 `args` 给出。 120 | 3. 拷贝构造函数(被禁用)(3),意味着 `std::thread` 对象不可拷贝构造。 121 | 4. Move 构造函数(4),move 构造函数(move 语义是 C++11 新出现的概念,详见附录),调用成功之后 `x` 不代表任何 `std::thread` 执行对象。 122 | 123 | >注意:可被 `joinable` 的 `std::thread` 对象必须在他们销毁之前被主线程 `join` 或者将其设置为 `detached`. 124 | 125 | std::thread 各种构造函数例子如下([参考](http://en.cppreference.com/w/cpp/thread/thread/thread)): 126 | 127 | #include 128 | #include 129 | #include 130 | #include 131 | #include 132 | #include 133 | 134 | void f1(int n) 135 | { 136 | for (int i = 0; i < 5; ++i) { 137 | std::cout << "Thread " << n << " executing\n"; 138 | std::this_thread::sleep_for(std::chrono::milliseconds(10)); 139 | } 140 | } 141 | 142 | void f2(int& n) 143 | { 144 | for (int i = 0; i < 5; ++i) { 145 | std::cout << "Thread 2 executing\n"; 146 | ++n; 147 | std::this_thread::sleep_for(std::chrono::milliseconds(10)); 148 | } 149 | } 150 | 151 | int main() 152 | { 153 | int n = 0; 154 | std::thread t1; // t1 is not a thread 155 | std::thread t2(f1, n + 1); // pass by value 156 | std::thread t3(f2, std::ref(n)); // pass by reference 157 | std::thread t4(std::move(t3)); // t4 is now running f2(). t3 is no longer a thread 158 | t2.join(); 159 | t4.join(); 160 | std::cout << "Final value of n is " << n << '\n'; 161 | } 162 | 163 | #### `std::thread` 赋值操作 #### 164 | 165 | 166 | 167 | 168 | 171 | 172 | 173 | 176 | 177 | 178 |
Move 赋值操作 (1) 169 | thread& operator=(thread&& rhs) noexcept; 170 |
拷贝赋值操作 [deleted] (2) 174 | thread& operator=(const thread&) = delete; 175 |
179 | 180 | 1. Move 赋值操作(1),如果当前对象不可 `joinable`,需要传递一个右值引用(`rhs`)给 `move` 赋值操作;如果当前对象可被 `joinable`,则会调用 `terminate`() 报错。 181 | 2. 拷贝赋值操作(2),被禁用,因此 `std::thread` 对象不可拷贝赋值。 182 | 183 | 请看下面的例子: 184 | 185 | #include 186 | #include 187 | 188 | #include // std::chrono::seconds 189 | #include // std::cout 190 | #include // std::thread, std::this_thread::sleep_for 191 | 192 | void thread_task(int n) { 193 | std::this_thread::sleep_for(std::chrono::seconds(n)); 194 | std::cout << "hello thread " 195 | << std::this_thread::get_id() 196 | << " paused " << n << " seconds" << std::endl; 197 | } 198 | 199 | int main(int argc, const char *argv[]) 200 | { 201 | std::thread threads[5]; 202 | std::cout << "Spawning 5 threads...\n"; 203 | for (int i = 0; i < 5; i++) { 204 | threads[i] = std::thread(thread_task, i + 1); 205 | } 206 | std::cout << "Done spawning threads! Now wait for them to join\n"; 207 | for (auto& t: threads) { 208 | t.join(); 209 | } 210 | std::cout << "All threads joined.\n"; 211 | 212 | return EXIT_SUCCESS; 213 | } 214 | 215 | ### 其他成员函数 ### 216 | 217 | > 本小节例子来自 [http://en.cppreference.com ](http://en.cppreference.com/w/cpp/thread/thread) 218 | 219 | - `get_id`: 获取线程 ID,返回一个类型为 `std::thread::id` 的对象。请看下面例子: 220 | 221 | #include 222 | #include 223 | #include 224 | 225 | void foo() 226 | { 227 | std::this_thread::sleep_for(std::chrono::seconds(1)); 228 | } 229 | 230 | int main() 231 | { 232 | std::thread t1(foo); 233 | std::thread::id t1_id = t1.get_id(); 234 | 235 | std::thread t2(foo); 236 | std::thread::id t2_id = t2.get_id(); 237 | 238 | std::cout << "t1's id: " << t1_id << '\n'; 239 | std::cout << "t2's id: " << t2_id << '\n'; 240 | 241 | t1.join(); 242 | t2.join(); 243 | } 244 | 245 | - `joinable`: 检查线程是否可被 join。检查当前的线程对象是否表示了一个活动的执行线程,由默认构造函数创建的线程是不能被 join 的。另外,如果某个线程 已经执行完任务,但是没有被 join 的话,该线程依然会被认为是一个活动的执行线程,因此也是可以被 join 的。 246 | 247 | #include 248 | #include 249 | #include 250 | 251 | void foo() 252 | { 253 | std::this_thread::sleep_for(std::chrono::seconds(1)); 254 | } 255 | 256 | int main() 257 | { 258 | std::thread t; 259 | std::cout << "before starting, joinable: " << t.joinable() << '\n'; 260 | 261 | t = std::thread(foo); 262 | std::cout << "after starting, joinable: " << t.joinable() << '\n'; 263 | 264 | t.join(); 265 | } 266 | 267 | - `join`: Join 线程,调用该函数会阻塞当前线程,直到由 `*this` 所标示的线程执行完毕 join 才返回。 268 | 269 | #include 270 | #include 271 | #include 272 | 273 | void foo() 274 | { 275 | // simulate expensive operation 276 | std::this_thread::sleep_for(std::chrono::seconds(1)); 277 | } 278 | 279 | void bar() 280 | { 281 | // simulate expensive operation 282 | std::this_thread::sleep_for(std::chrono::seconds(1)); 283 | } 284 | 285 | int main() 286 | { 287 | std::cout << "starting first helper...\n"; 288 | std::thread helper1(foo); 289 | 290 | std::cout << "starting second helper...\n"; 291 | std::thread helper2(bar); 292 | 293 | std::cout << "waiting for helpers to finish..." << std::endl; 294 | helper1.join(); 295 | helper2.join(); 296 | 297 | std::cout << "done!\n"; 298 | } 299 | 300 | - `detach`: Detach 线程。 301 | 将当前线程对象所代表的执行实例与该线程对象分离,使得线程的执行可以单独进行。一旦线程执行完毕,它所分配的资源将会被释放。 302 | 303 | 调用 detach 函数之后: 304 | 305 | 1. `*this` 不再代表任何的线程执行实例。 306 | 2. joinable() == false 307 | 3. get_id() == std::thread::id() 308 | 309 | 另外,如果出错或者 `joinable() == false`,则会抛出 `std::system_error`. 310 | 311 | #include 312 | #include 313 | #include 314 | 315 | void independentThread() 316 | { 317 | std::cout << "Starting concurrent thread.\n"; 318 | std::this_thread::sleep_for(std::chrono::seconds(2)); 319 | std::cout << "Exiting concurrent thread.\n"; 320 | } 321 | 322 | void threadCaller() 323 | { 324 | std::cout << "Starting thread caller.\n"; 325 | std::thread t(independentThread); 326 | t.detach(); 327 | std::this_thread::sleep_for(std::chrono::seconds(1)); 328 | std::cout << "Exiting thread caller.\n"; 329 | } 330 | 331 | int main() 332 | { 333 | threadCaller(); 334 | std::this_thread::sleep_for(std::chrono::seconds(5)); 335 | } 336 | 337 | - `swap`: Swap 线程,交换两个线程对象所代表的底层句柄(underlying handles)。 338 | 339 | #include 340 | #include 341 | #include 342 | 343 | void foo() 344 | { 345 | std::this_thread::sleep_for(std::chrono::seconds(1)); 346 | } 347 | 348 | void bar() 349 | { 350 | std::this_thread::sleep_for(std::chrono::seconds(1)); 351 | } 352 | 353 | int main() 354 | { 355 | std::thread t1(foo); 356 | std::thread t2(bar); 357 | 358 | std::cout << "thread 1 id: " << t1.get_id() << std::endl; 359 | std::cout << "thread 2 id: " << t2.get_id() << std::endl; 360 | 361 | std::swap(t1, t2); 362 | 363 | std::cout << "after std::swap(t1, t2):" << std::endl; 364 | std::cout << "thread 1 id: " << t1.get_id() << std::endl; 365 | std::cout << "thread 2 id: " << t2.get_id() << std::endl; 366 | 367 | t1.swap(t2); 368 | 369 | std::cout << "after t1.swap(t2):" << std::endl; 370 | std::cout << "thread 1 id: " << t1.get_id() << std::endl; 371 | std::cout << "thread 2 id: " << t2.get_id() << std::endl; 372 | 373 | t1.join(); 374 | t2.join(); 375 | } 376 | 377 | 执行结果如下: 378 | 379 | thread 1 id: 1892 380 | thread 2 id: 2584 381 | after std::swap(t1, t2): 382 | thread 1 id: 2584 383 | thread 2 id: 1892 384 | after t1.swap(t2): 385 | thread 1 id: 1892 386 | thread 2 id: 2584 387 | 388 | - `native_handle`: 返回 native handle(由于 `std::thread` 的实现和操作系统相关,因此该函数返回与 `std::thread` 具体实现相关的线程句柄,例如在符合 Posix 标准的平台下(如 Unix/Linux)是 Pthread 库)。 389 | 390 | #include 391 | #include 392 | #include 393 | #include 394 | #include 395 | 396 | std::mutex iomutex; 397 | void f(int num) 398 | { 399 | std::this_thread::sleep_for(std::chrono::seconds(1)); 400 | 401 | sched_param sch; 402 | int policy; 403 | pthread_getschedparam(pthread_self(), &policy, &sch); 404 | std::lock_guard lk(iomutex); 405 | std::cout << "Thread " << num << " is executing at priority " 406 | << sch.sched_priority << '\n'; 407 | } 408 | 409 | int main() 410 | { 411 | std::thread t1(f, 1), t2(f, 2); 412 | 413 | sched_param sch; 414 | int policy; 415 | pthread_getschedparam(t1.native_handle(), &policy, &sch); 416 | sch.sched_priority = 20; 417 | if(pthread_setschedparam(t1.native_handle(), SCHED_FIFO, &sch)) { 418 | std::cout << "Failed to setschedparam: " << std::strerror(errno) << '\n'; 419 | } 420 | 421 | t1.join(); 422 | t2.join(); 423 | } 424 | 425 | 执行结果如下: 426 | 427 | Thread 2 is executing at priority 0 428 | Thread 1 is executing at priority 20 429 | 430 | - `hardware_concurrency` [static]: 检测硬件并发特性,返回当前平台的线程实现所支持的线程并发数目,但返回值仅仅只作为系统提示(hint)。 431 | 432 | #include 433 | #include 434 | 435 | int main() { 436 | unsigned int n = std::thread::hardware_concurrency(); 437 | std::cout << n << " concurrent threads are supported.\n"; 438 | } 439 | 440 | ## `std::this_thread` 命名空间中相关辅助函数介绍 ## 441 | 442 | - get_id: 获取线程 ID。 443 | 444 | #include 445 | #include 446 | #include 447 | #include 448 | 449 | std::mutex g_display_mutex; 450 | 451 | void foo() 452 | { 453 | std::thread::id this_id = std::this_thread::get_id(); 454 | 455 | g_display_mutex.lock(); 456 | std::cout << "thread " << this_id << " sleeping...\n"; 457 | g_display_mutex.unlock(); 458 | 459 | std::this_thread::sleep_for(std::chrono::seconds(1)); 460 | } 461 | 462 | int main() 463 | { 464 | std::thread t1(foo); 465 | std::thread t2(foo); 466 | 467 | t1.join(); 468 | t2.join(); 469 | } 470 | 471 | - yield: 当前线程放弃执行,操作系统调度另一线程继续执行。 472 | 473 | #include 474 | #include 475 | #include 476 | 477 | // "busy sleep" while suggesting that other threads run 478 | // for a small amount of time 479 | void little_sleep(std::chrono::microseconds us) 480 | { 481 | auto start = std::chrono::high_resolution_clock::now(); 482 | auto end = start + us; 483 | do { 484 | std::this_thread::yield(); 485 | } while (std::chrono::high_resolution_clock::now() < end); 486 | } 487 | 488 | int main() 489 | { 490 | auto start = std::chrono::high_resolution_clock::now(); 491 | 492 | little_sleep(std::chrono::microseconds(100)); 493 | 494 | auto elapsed = std::chrono::high_resolution_clock::now() - start; 495 | std::cout << "waited for " 496 | << std::chrono::duration_cast(elapsed).count() 497 | << " microseconds\n"; 498 | } 499 | 500 | - sleep_until: 线程休眠至某个指定的时刻(time point),该线程才被重新唤醒。 501 | 502 | template< class Clock, class Duration > 503 | void sleep_until( const std::chrono::time_point& sleep_time ); 504 | 505 | 506 | 507 | - sleep_for: 线程休眠某个指定的时间片(time span),该线程才被重新唤醒,不过由于线程调度等原因,实际休眠时间可能比 `sleep_duration` 所表示的时间片更长。 508 | 509 | template< class Rep, class Period > 510 | void sleep_for( const std::chrono::duration& sleep_duration ); 511 | 512 | #include 513 | #include 514 | #include 515 | 516 | int main() 517 | { 518 | std::cout << "Hello waiter" << std::endl; 519 | std::chrono::milliseconds dura( 2000 ); 520 | std::this_thread::sleep_for( dura ); 521 | std::cout << "Waited 2000 ms\n"; 522 | } 523 | 524 | 执行结果如下: 525 | 526 | Hello waiter 527 | Waited 2000 ms -------------------------------------------------------------------------------- /zh/chapter3-Thread/README.md: -------------------------------------------------------------------------------- 1 | # 本章导读 # 2 | 3 | 本章主要内容如下: 4 | 5 | - `` 头文件中的类及相关函数的介绍,包括创建新的线程对象、线程对象的控制和管理、与线程相关的异常处理等。 6 | - `std::thread` 与 Pthread 等线程库的对比,包括两者编程接口的差异及其他细节。 7 | - `std::thread` 应用实例。 8 | 9 | # 第三章 线程详解 # 10 | 11 | 12 | ## [3.1 `` 头文件摘要](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter3-Thread/Introduction-to-Thread.md#thread-%E5%A4%B4%E6%96%87%E4%BB%B6%E6%91%98%E8%A6%81) ## 13 | ### [3.1.1 `std::thread` 类摘要](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter3-Thread/Introduction-to-Thread.md#stdthread-%E7%B1%BB%E6%91%98%E8%A6%81) ### 14 | 15 | ## [3.2 `std::thread` 详解](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter3-Thread/Introduction-to-Thread.md#stdthread-%E8%AF%A6%E8%A7%A3) ## 16 | ### [3.2.1 `std::thread` 构造和赋值](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter3-Thread/Introduction-to-Thread.md#stdthread-%E6%9E%84%E9%80%A0%E5%92%8C%E8%B5%8B%E5%80%BC) ### 17 | #### [3.2.1.1 `std::thread` 构造函数](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter3-Thread/Introduction-to-Thread.md#stdthread-%E6%9E%84%E9%80%A0%E5%87%BD%E6%95%B0) #### 18 | #### [3.2.2.2 `std::thread` 赋值操作](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter3-Thread/Introduction-to-Thread.md#stdthread-%E8%B5%8B%E5%80%BC%E6%93%8D%E4%BD%9C) #### 19 | ### [3.2.2 其他成员函数](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter3-Thread/Introduction-to-Thread.md#%E5%85%B6%E4%BB%96%E6%88%90%E5%91%98%E5%87%BD%E6%95%B0) ### 20 | 21 | ## [3.3 `std::this_thread` 命名空间中相关辅助函数介绍](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter3-Thread/Introduction-to-Thread.md#stdthis_thread-%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4%E4%B8%AD%E7%9B%B8%E5%85%B3%E8%BE%85%E5%8A%A9%E5%87%BD%E6%95%B0%E4%BB%8B%E7%BB%8D) ## 22 | 23 | ## 3.4 `std::thread` 与 Pthread 对比 ## 24 | 25 | ## [3.5 资料汇](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter3-Thread/web-resources.md "资料汇") ## -------------------------------------------------------------------------------- /zh/chapter3-Thread/web-resources.md: -------------------------------------------------------------------------------- 1 | # C++ 多线程编程资料汇 # 2 | 3 | - C++11 - the new ISO C++ standard: [http://www.stroustrup.com/C++11FAQ.html](http://www.stroustrup.com/C++11FAQ.html) 4 | - [http://en.cppreference.com/w/cpp/thread/thread](http://en.cppreference.com/w/cpp/thread/thread) 5 | - [http://www.cplusplus.com/reference/thread/](http://www.cplusplus.com/reference/thread/) 6 | - [http://gcc.gnu.org/projects/cxx0x.html](http://gcc.gnu.org/projects/cxx0x.html) 7 | - C++0x/C++11 Support in GCC: [http://gcc.gnu.org/projects/cxx0x.html](http://gcc.gnu.org/projects/cxx0x.html) 8 | - Chapter 30. Thread 4.1.0: [http://www.boost.org/doc/libs/1_54_0/doc/html/thread.html](http://www.boost.org/doc/libs/1_54_0/doc/html/thread.html) 9 | - C++11 Concurrency – Part 1 : Start Threads: [http://www.baptiste-wicht.com/2012/03/cpp11-concurrency-part1-start-threads/](http://www.baptiste-wicht.com/2012/03/cpp11-concurrency-part1-start-threads/) 10 | - Multithreading in C++0x part 1: Starting Threads: [http://www.justsoftwaresolutions.co.uk/threading/multithreading-in-c++0x-part-1-starting-threads.html](http://www.justsoftwaresolutions.co.uk/threading/multithreading-in-c++0x-part-1-starting-threads.html) 11 | - Multithreading in C++0x part 2: Starting Threads with Function Objects and Arguments: [http://www.justsoftwaresolutions.co.uk/threading/multithreading-in-c++0x-part-2-function-objects-and-arguments.html](http://www.justsoftwaresolutions.co.uk/threading/multithreading-in-c++0x-part-2-function-objects-and-arguments.html) 12 | - Multithreading in C++0x part 3: Starting Threads with Member Functions and Reference Arguments: [http://www.justsoftwaresolutions.co.uk/threading/multithreading-in-c++0x-part-3.html](http://www.justsoftwaresolutions.co.uk/threading/multithreading-in-c++0x-part-3.html) 13 | - C++11 multithreading tutorial: [http://solarianprogrammer.com/2011/12/16/cpp-11-thread-tutorial/](http://solarianprogrammer.com/2011/12/16/cpp-11-thread-tutorial/) 14 | - C++11 multithreading tutorial - part 2: [http://solarianprogrammer.com/2012/02/27/cpp-11-thread-tutorial-part-2/](http://solarianprogrammer.com/2012/02/27/cpp-11-thread-tutorial-part-2/) 15 | - C++11 multithreading tutorial - part 3: [http://solarianprogrammer.com/2012/05/09/cpp-11-thread-tutorial-part-3/](http://solarianprogrammer.com/2012/05/09/cpp-11-thread-tutorial-part-3/) 16 | - C++11 threads, locks and condition variables: [http://www.codeproject.com/Articles/598695/Cplusplus11-threads-locks-and-condition-variables](http://www.codeproject.com/Articles/598695/Cplusplus11-threads-locks-and-condition-variables) 17 | - C++11: std::thread: [http://oopscenities.net/2012/11/16/c11-stdthread-2/](http://oopscenities.net/2012/11/16/c11-stdthread-2/) 18 | - C++11 Concurrency – Part 1 : Start Threads: [http://dotnet.dzone.com/articles/c11-concurrency-%E2%80%93-part-1-start](http://dotnet.dzone.com/articles/c11-concurrency-%E2%80%93-part-1-start) 19 | - Multi Threading Tutorials: [http://www.mario-konrad.ch/wiki/doku.php?id=programming:multithreading:start](http://www.mario-konrad.ch/wiki/doku.php?id=programming:multithreading:start) -------------------------------------------------------------------------------- /zh/chapter4-Mutex/4.1 Mutex-header-synopsis.md: -------------------------------------------------------------------------------- 1 | Mutex 又称互斥量,C++11 中与 Mutex 相关的类(包括锁类型)和函数都声明在 `` 头文件中,所以如果你在程序里面使用 `std::mutex` 及相关类型和辅助函数,就必须包含 `` 头文件。 2 | 3 | C++11 中定义了如下与互斥量和锁相关的类: 4 | 5 | - Mutex 系列类(四种),C++11 标准中规定的与互斥量相关的类包括: 6 | 7 | 1. `std::mutex`,最基本的 Mutex 类,该类提供了最基本的上锁和解锁操作。同时,基本的互斥量不允许某个线程在已获得互斥量的情况下重复对该互斥量进行上锁操作,所以重复上锁将会导致死锁(结果通常未定义的)。 8 | 2. `std::recursive_mutex`,递归 Mutex 类,与 `std::mutex` 功能基本相同,但是允许互斥量的拥有者(通常是某个线程)重复对该互斥量进行上锁操作而不会产生死锁,但必须保证上锁和解锁的次数相同。 9 | 3. `std::time_mutex`,定时 Mutex 类,与 `std::mutex` 功能基本相同,但是提供了两个额外的定时上锁操作,`try_lock_for` 和 `try_lock_until`,即某个线程在规定的时间内对互斥量进行上锁操作,如果在规定的时间内获得了锁则返回 `true`, 超时则返回 `false`,在本章后面的内容中我会介绍`try_lock_for` 和 `try_lock_until`两个上锁函数之间细微的差异。 10 | 4. `std::recursive_timed_mutex`,定时递归 Mutex 类,既提供了重复上锁功能,又提供了定时上锁的特性(即在规定的时间内没有获得锁则返回 `false`),相当于 `std::recursive_mutex` 和 `std::time_mutex` 的组合。 11 | 12 | - Lock 类(两种),C++11 标准中定义了两种与互斥量相关的 RAII 技术。(RAII:Resource Acquisition Is Initialization,资源获取即初始化,参见维基百科中与 [RAII](http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization "RAII") 相关的定义) 13 | 14 | 1. `std::lock_guard`,与 Mutex RAII 相关,方便线程对互斥量上锁。 15 | 2. `std::unique_lock`,与 Mutex RAII 相关,方便线程对互斥量上锁,但提供了更好的上锁和解锁控制。 16 | 17 | - 其他类型 18 | 19 | 1. `std::once_flag`,`call_once` 辅助函数会使用到该类型的对象。 20 | 2. `std::adopt_lock_t`,一个空的标记类,定义如下:`struct adopt_lock_t {};`,该类型的常量对象`adopt_lock`(`adopt_lock` 是一个常量对象,定义如下:`constexpr adopt_lock_t adopt_lock {};`,`constexpr` 是 C++11 中的新关键字) 通常作为参数传入给 `unique_lock` 或 `lock_guard` 的构造函数。 21 | 3. `std::defer_lock_t`,一个空的标记类,定义如下:`struct defer_lock_t {};`,该类型的常量对象`defer_lock`(`defer_lock` 是一个常量对象,定义如下:`constexpr defer_lock_t defer_lock {};`) 通常作为参数传入给 `unique_lock` 或 `lock_guard` 的构造函数。。 22 | 4. `std::try_to_lock_t`,一个空的标记类,定义如下:`struct try_to_lock_t {};`,该类型的常量对象`try_to_lock`(`try_to_lock` 是一个常量对象,定义如下:`constexpr try_to_lock_t try_to_lock {};`) 通常作为参数传入给 `unique_lock` 或 `lock_guard` 的构造函数。。 23 | 24 | - 辅助函数 25 | 26 | 1. `std::try_lock`,尝试同时对多个互斥量上锁。 27 | 2. `std::lock`,同时对多个互斥量上锁。 28 | 3. `std::call_once`,如果多个线程需要同时调用某个函数,`call_once` 可以保证多个线程对该函数只调用一次。 29 | 30 | 本章后面会对以上类型和函数进行详细介绍,我们首先来看 `` 头文件中各个类型的摘要吧。 31 | 32 | ## 4.1 `` 头文件摘要 ## 33 | 34 | `` 头文件摘要如下: 35 | 36 | namespace std { 37 | 38 | class mutex; 39 | class recursive_mutex; 40 | class timed_mutex; 41 | class recursive_timed_mutex; 42 | 43 | struct defer_lock_t { }; 44 | struct try_to_lock_t { }; 45 | struct adopt_lock_t { }; 46 | 47 | constexpr defer_lock_t defer_lock { }; 48 | constexpr try_to_lock_t try_to_lock { }; 49 | constexpr adopt_lock_t adopt_lock { }; 50 | 51 | template class lock_guard; 52 | template class unique_lock; 53 | 54 | template 55 | void swap(unique_lock& x, unique_lock& y); 56 | 57 | template int try_lock(L1&, L2&, L3&...); 58 | template void lock(L1&, L2&, L3&...); 59 | 60 | struct once_flag { 61 | constexpr once_flag() noexcept; 62 | once_flag(const once_flag&) = delete; 63 | once_flag& operator=(const once_flag&) = delete; 64 | }; 65 | 66 | template 67 | void call_once(once_flag& flag, Callable func, Args&&... args); 68 | } 69 | 70 | ### 4.1.1 `std::mutex` 类摘要 ### 71 | 72 | namespace std { 73 | class mutex { 74 | public: 75 | constexpr mutex(); 76 | ~mutex(); 77 | mutex(const mutex&) = delete; 78 | mutex& operator=(const mutex&) = delete; 79 | 80 | void lock(); 81 | bool try_lock() noexcept; 82 | void unlock() noexcept; 83 | 84 | typedef implementation-defined native_handle_type; 85 | native_handle_type native_handle(); 86 | }; 87 | } 88 | 89 | ### 4.1.2 `std::recursive_mutex` 类摘要 ### 90 | 91 | namespace std { 92 | class recursive_mutex { 93 | public: 94 | recursive_mutex(); 95 | ~recursive_mutex(); 96 | recursive_mutex(const recursive_mutex&) = delete; 97 | recursive_mutex& operator=(const recursive_mutex&) = delete; 98 | 99 | void lock(); 100 | bool try_lock() noexcept; 101 | void unlock() noexcept; 102 | 103 | typedef implementation-defined native_handle_type; 104 | native_handle_type native_handle(); 105 | }; 106 | } 107 | 108 | ### 4.1.3 `std::timed_mutex` 类摘要 ### 109 | 110 | namespace std { 111 | class timed_mutex { 112 | public: 113 | timed_mutex(); 114 | ~timed_mutex(); 115 | timed_mutex(const timed_mutex&) = delete; 116 | timed_mutex& operator=(const timed_mutex&) = delete; 117 | 118 | void lock(); 119 | bool try_lock(); 120 | template 121 | bool try_lock_for(const chrono::duration& rel_time) noexcept; 122 | template 123 | bool try_lock_until(const chrono::time_point& abs_time) noexcept; 124 | void unlock(); 125 | 126 | typedef implementation-defined native_handle_type; 127 | native_handle_type native_handle(); 128 | }; 129 | } 130 | 131 | ### 4.1.4 `std::recursive_timed_mutex` 类摘要 ### 132 | 133 | namespace std { 134 | class recursive_timed_mutex { 135 | public: 136 | recursive_timed_mutex(); 137 | ~recursive_timed_mutex(); 138 | recursive_timed_mutex(const recursive_timed_mutex&) = delete; 139 | recursive_timed_mutex& operator=(const recursive_timed_mutex&) = delete; 140 | 141 | void lock(); 142 | bool try_lock(); 143 | template 144 | bool try_lock_for(const chrono::duration& rel_time) noexcept; 145 | template 146 | bool try_lock_until(const chrono::time_point& abs_time) noexcept; 147 | void unlock(); 148 | 149 | typedef implementation-defined native_handle_type; 150 | native_handle_type native_handle(); 151 | }; 152 | } 153 | 154 | ### 4.1.5 `std::lock_guard` 类摘要 ### 155 | 156 | namespace std { 157 | template 158 | class lock_guard { 159 | public: 160 | typedef Mutex mutex_type; 161 | explicit lock_guard(mutex_type& m); 162 | lock_guard(mutex_type& m, adopt_lock_t) noexcept; 163 | ~lock_guard(); 164 | lock_guard(lock_guard const&) = delete; 165 | lock_guard& operator=(lock_guard const&) = delete; 166 | private: 167 | mutex_type& pm; // exposition only 168 | }; 169 | } 170 | 171 | ### 4.1.6 `std::unique_lock` 类摘要 ### 172 | 173 | namespace std { 174 | template 175 | class unique_lock { 176 | public: 177 | typedef Mutex mutex_type; 178 | // 构造/拷贝/析构: 179 | unique_lock() noexcept; 180 | explicit unique_lock(mutex_type& m); 181 | unique_lock(mutex_type& m, defer_lock_t) noexcept; 182 | unique_lock(mutex_type& m, try_to_lock_t) noexcept; 183 | unique_lock(mutex_type& m, adopt_lock_t) noexcept; 184 | template 185 | unique_lock(mutex_type& m, const chrono::time_point& abs_time) noexcept; 186 | template 187 | unique_lock(mutex_type& m, const chrono::duration& rel_time) noexcept; 188 | ~unique_lock(); 189 | unique_lock(unique_lock const&) = delete; 190 | unique_lock& operator=(unique_lock const&) = delete; 191 | unique_lock(unique _lock&& u) noexcept; 192 | unique_lock& operator=(unique_lock&& u) noexcept; 193 | 194 | // 上锁操作: 195 | void lock(); 196 | bool try_lock(); 197 | template 198 | bool try_lock_for(const chrono::duration& rel_time); 199 | template 200 | bool try_lock_until(const chrono::time_point& abs_time); 201 | void unlock(); 202 | 203 | // 修改操作 204 | void swap(unique_lock& u) noexcept; 205 | mutex_type *release() noexcept; 206 | 207 | // observers: 208 | bool owns_lock() const noexcept; 209 | explicit operator bool () const noexcept; 210 | mutex_type* mutex() const noexcept; 211 | private: 212 | mutex_type *pm; // exposition only 213 | bool owns; // exposition only 214 | }; 215 | 216 | template 217 | void swap(unique_lock& x, unique_lock& y) noexcept; 218 | } 219 | -------------------------------------------------------------------------------- /zh/chapter4-Mutex/4.2 Mutex-tutorial.md: -------------------------------------------------------------------------------- 1 | 本小节介绍 std::mutex 的用法。 2 | 3 | ## 4.2 互斥量详解 ## 4 | 5 | ### 4.2 `std::mutex` 介绍 ### 6 | 7 | `std::mutex` 是 C++11 中最基本的互斥量,`std::mutex` 对象提供了独占所有权的特性——即不支持递归地对 `std::mutex` 对象上锁,而 `std::recursive_lock` 则可以递归地对互斥量对象上锁。 8 | 9 | ### `std::mutex` 的成员函数 ### 10 | 11 | - 构造函数,std::mutex不允许拷贝构造,也不允许 move 拷贝,最初产生的 mutex 对象是处于 unlocked 状态的。 12 | - lock(),调用线程将锁住该互斥量。线程调用该函数会发生下面 3 种情况:(1). 如果该互斥量当前没有被锁住,则调用线程将该互斥量锁住,直到调用 unlock之前,该线程一直拥有该锁。(2). 如果当前互斥量被其他线程锁住,则当前的调用线程被阻塞住。(3). 如果当前互斥量被当前调用线程锁住,则会产生死锁(deadlock)。 13 | - unlock(), 解锁,释放对互斥量的所有权。 14 | - try_lock(),尝试锁住互斥量,如果互斥量被其他线程占有,则当前线程也不会被阻塞。线程调用该函数也会出现下面 3 种情况, 15 | 16 | 1. 如果当前互斥量没有被其他线程占有,则该线程锁住互斥量,直到该线程调用 unlock 释放互斥量。 17 | 2. 如果当前互斥量被其他线程锁住,则当前调用线程返回 false,而并不会被阻塞掉。 18 | 3. 如果当前互斥量被当前调用线程锁住,则会产生死锁(deadlock)。 19 | 20 | 下面给出一个与 std::mutex 的小例子([参考](http://www.cplusplus.com/reference/mutex/mutex/try_lock/)) 21 | 22 | #include // std::cout 23 | #include // std::thread 24 | #include // std::mutex 25 | 26 | volatile int counter(0); // non-atomic counter 27 | std::mutex mtx; // locks access to counter 28 | 29 | void attempt_10k_increases() { 30 | for (int i=0; i<10000; ++i) { 31 | if (mtx.try_lock()) { // only increase if currently not locked: 32 | ++counter; 33 | mtx.unlock(); 34 | } 35 | } 36 | } 37 | 38 | int main (int argc, const char* argv[]) { 39 | std::thread threads[10]; 40 | for (int i=0; i<10; ++i) 41 | threads[i] = std::thread(attempt_10k_increases); 42 | 43 | for (auto& th : threads) th.join(); 44 | std::cout << counter << " successful increases of the counter.\n"; 45 | 46 | return 0; 47 | } 48 | 49 | ### `std::recursive_mutex` 介绍 ### 50 | 51 | `std::recursive_mutex` 与 `std::mutex `一样,也是一种可以被上锁的对象,但是和 `std::mutex` 不同的是,`std::recursive_mutex` 允许同一个线程对互斥量多次上锁(即递归上锁),来获得对互斥量对象的多层所有权,`std::recursive_mutex` 释放互斥量时需要调用与该锁层次深度相同次数的 `unlock()`,可理解为 `lock()` 次数和 `unlock()` 次数相同,除此之外,`std::recursive_mutex` 的特性和 `std::mutex` 大致相同。 52 | 53 | #include // std::cout 54 | #include // std::thread 55 | #include // std::mutex 56 | 57 | class counter 58 | { 59 | public: 60 | counter() : count(0) { } 61 | 62 | int add(int val) { 63 | std::recursive_mutex::scoped_lock scoped_lock(mutex); 64 | count += val; 65 | return count; 66 | } 67 | int increment() { 68 | std::recursive_mutex::scoped_lock scoped_lock(mutex); 69 | return add(1); 70 | } 71 | 72 | private: 73 | boost::recursive_mutex mutex; 74 | int count; 75 | }; 76 | 77 | counter c; 78 | 79 | void change_count(void*) 80 | { 81 | std::cout << "count == " << c.increment() << std::endl; 82 | } 83 | 84 | int main(int, char*[]) 85 | { 86 | 87 | std::thread threads[10]; 88 | // spawn 10 threads: 89 | for (int i=0; i<10; ++i) 90 | threads[i] = std::thread(change_count, 0); 91 | 92 | for (auto& th : threads) th.join(); 93 | 94 | return 0; 95 | } 96 | 97 | ### std::time_mutex 介绍 ### 98 | 99 | `std::time_mutex` 比 `std::mutex` 多了两个成员函数,`try_lock_for()`,`try_lock_until()`。 100 | 101 | `try_lock_for` 函数接受一个时间范围,表示在这一段时间范围之内线程如果没有获得锁则被阻塞住(与 `std::mutex` 的 `try_lock()` 不同,try_lock 如果被调用时没有获得锁则直接返回 `false`),如果在此期间其他线程释放了锁,则该线程可以获得对互斥量的锁,如果超时(即在指定时间内还是没有获得锁),则返回 `false`。 102 | 103 | `try_lock_until` 函数则接受一个时间点作为参数,在指定时间点未到来之前线程如果没有获得锁则被阻塞住,如果在此期间其他线程释放了锁,则该线程可以获得对互斥量的锁,如果超时(即在指定时间内还是没有获得锁),则返回 `false`。 104 | 105 | 下面的小例子说明了 `std::time_mutex` 的用法([参考](http://www.cplusplus.com/reference/mutex/timed_mutex/try_lock_for/))。 106 | 107 | #include // std::cout 108 | #include // std::chrono::milliseconds 109 | #include // std::thread 110 | #include // std::timed_mutex 111 | 112 | std::timed_mutex mtx; 113 | 114 | void fireworks() { 115 | // waiting to get a lock: each thread prints "-" every 200ms: 116 | while (!mtx.try_lock_for(std::chrono::milliseconds(200))) { 117 | std::cout << "-"; 118 | } 119 | 120 | // got a lock! - wait for 1s, then this thread prints "*" 121 | std::this_thread::sleep_for(std::chrono::milliseconds(1000)); 122 | std::cout << "*\n"; 123 | mtx.unlock(); 124 | } 125 | 126 | int main () 127 | { 128 | std::thread threads[10]; 129 | // spawn 10 threads: 130 | for (int i=0; i<10; ++i) 131 | threads[i] = std::thread(fireworks); 132 | 133 | for (auto& th : threads) th.join(); 134 | 135 | return 0; 136 | } 137 | 138 | ### `std::recursive_timed_mutex` 介绍 ### 139 | 140 | 和 `std:recursive_mutex` 与 `std::mutex` 的关系一样,`std::recursive_timed_mutex` 的特性也可以从 `std::timed_mutex` 推导出来,感兴趣的同鞋可以自行查阅。;-) 141 | 142 | ### std::lock_guard 介绍 ### 143 | 144 | 与 Mutex RAII 相关,方便线程对互斥量上锁。例子([参考](http://www.cplusplus.com/reference/mutex/lock_guard/)): 145 | 146 | #include // std::cout 147 | #include // std::thread 148 | #include // std::mutex, std::lock_guard 149 | #include // std::logic_error 150 | 151 | std::mutex mtx; 152 | 153 | void print_even (int x) { 154 | if (x%2==0) std::cout << x << " is even\n"; 155 | else throw (std::logic_error("not even")); 156 | } 157 | 158 | void print_thread_id (int id) { 159 | try { 160 | // using a local lock_guard to lock mtx guarantees unlocking on destruction / exception: 161 | std::lock_guard lck (mtx); 162 | print_even(id); 163 | } 164 | catch (std::logic_error&) { 165 | std::cout << "[exception caught]\n"; 166 | } 167 | } 168 | 169 | int main () 170 | { 171 | std::thread threads[10]; 172 | // spawn 10 threads: 173 | for (int i=0; i<10; ++i) 174 | threads[i] = std::thread(print_thread_id,i+1); 175 | 176 | for (auto& th : threads) th.join(); 177 | 178 | return 0; 179 | } 180 | 181 | ### `std::unique_lock` 介绍 ### 182 | 183 | 与 Mutex RAII 相关,方便线程对互斥量上锁,但提供了更好的上锁和解锁控制。例子([参考](http://www.cplusplus.com/reference/mutex/unique_lock/)): 184 | 185 | #include // std::cout 186 | #include // std::thread 187 | #include // std::mutex, std::unique_lock 188 | 189 | std::mutex mtx; // mutex for critical section 190 | 191 | void print_block (int n, char c) { 192 | // critical section (exclusive access to std::cout signaled by lifetime of lck): 193 | std::unique_lock lck (mtx); 194 | for (int i=0; i` 头文件中的类及相关函数的介绍,包括互斥量、各种锁类型的介绍,以及 lock, try_lock,call_once 等函数的用法。 6 | - `std::mutex` 与 Pthread 等线程库中的 `pthread_mutex_t` 类型的对比,包括两者编程接口的差异及其他细节。 7 | - `std::mutex` 和 `std::lock_guard`、`std::unique_lock` 应用实例。 8 | 9 | # 第四章 互斥量与锁 # 10 | 11 | ## [4.1 `` 头文件摘要](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter4-Mutex/4.1%20Mutex-header-synopsis.md) ## 12 | ### [4.1.1 `std::mutex` 类摘要](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter4-Mutex/4.1%20Mutex-header-synopsis.md#411-stdmutex-%E7%B1%BB%E6%91%98%E8%A6%81) ### 13 | ### [4.1.2 `std::recursive_mutex` 类摘要](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter4-Mutex/4.1%20Mutex-header-synopsis.md#412-stdrecursive_mutex-%E7%B1%BB%E6%91%98%E8%A6%81) ### 14 | ### [4.1.3 `std::timed_mutex` 类摘要](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter4-Mutex/4.1%20Mutex-header-synopsis.md#413-stdtimed_mutex-%E7%B1%BB%E6%91%98%E8%A6%81) ### 15 | ### [4.1.4 `std::recursive_timed_mutex` 类摘要](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter4-Mutex/4.1%20Mutex-header-synopsis.md#414-stdrecursive_timed_mutex-%E7%B1%BB%E6%91%98%E8%A6%81) ### 16 | ### [4.1.5 `std::lock_guard` 类摘要](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter4-Mutex/4.1%20Mutex-header-synopsis.md#415-stdlock_guard-%E7%B1%BB%E6%91%98%E8%A6%81) ### 17 | ### [4.1.6 `std::unique_lock` 类摘要](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter4-Mutex/4.1%20Mutex-header-synopsis.md#416-stdunique_lock-%E7%B1%BB%E6%91%98%E8%A6%81) ### 18 | 19 | ## [4.2 互斥量详解](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter4-Mutex/4.2%20Mutex-tutorial.md) ## 20 | ### 4.2.1 `std::mutex` 类型介绍 ### 21 | #### 4.2.1.1 `std::mutex` 构造函数 #### 22 | #### 4.2.1.2 `std::mutex` 赋值操作 #### 23 | #### 4.2.1.3 其他成员函数 #### 24 | ### 4.2.2 `std::recursive_mutex` 类型介绍 ### 25 | ### 4.2.3 `std::timed_mutex` 类型介绍 ### 26 | ### 4.2.4 `std::recursive_timed_mutex` 类型介绍 ### 27 | 28 | ## [4.3 锁类型详解](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter4-Mutex/4.3%20Lock-tutorial.md) ## 29 | ### 4.3.1 `std::lock_guard` 类型介绍 ### 30 | ### 4.3.2 `std::unique_lock` 类型介绍 ### 31 | 32 | ## 4.4 辅助函数介绍 ## 33 | 34 | ## 4.5 `std::mutex` 与 Pthread 对比 ## 35 | 36 | ## [4.6 资料汇](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter4-Mutex/web-resources.md) ## -------------------------------------------------------------------------------- /zh/chapter4-Mutex/web-resources.md: -------------------------------------------------------------------------------- 1 | # C++ 互斥量与锁资料汇 # 2 | 3 | - C++11 - the new ISO C++ standard: [http://www.stroustrup.com/C++11FAQ.html](http://www.stroustrup.com/C++11FAQ.html) 4 | - [http://en.cppreference.com/w/cpp/thread/mutex](http://en.cppreference.com/w/cpp/thread/mutex) 5 | - [http://www.cplusplus.com/reference/mutex/mutex/](http://www.cplusplus.com/reference/mutex/mutex/) 6 | - C++11 Concurrency Tutorial – Part 3: Advanced locking and condition variables: [http://www.baptiste-wicht.com/2012/04/c11-concurrency-tutorial-advanced-locking-and-condition-variables/](http://www.baptiste-wicht.com/2012/04/c11-concurrency-tutorial-advanced-locking-and-condition-variables/) 7 | - C++11 Concurrency Tutorial: 7. Mutex, Lock, and Monitor: [http://bartoszmilewski.com/2011/10/24/c11-concurrency-tutorial-7-mutex-lock-and-monitor/](http://bartoszmilewski.com/2011/10/24/c11-concurrency-tutorial-7-mutex-lock-and-monitor/) 8 | - [http://www.justsoftwaresolutions.co.uk/files/c++11_concurrency.pdf](http://www.justsoftwaresolutions.co.uk/files/c++11_concurrency.pdf) 9 | - Comparing the performance of atomic, spinlock and mutex: [http://demin.ws/blog/english/2012/05/05/atomic-spinlock-mutex/](http://demin.ws/blog/english/2012/05/05/atomic-spinlock-mutex/) 10 | - Multithreading in C++0x part 4: Protecting Shared Data: [http://www.justsoftwaresolutions.co.uk/threading/multithreading-in-c++0x-part-4-protecting-shared-data.html](http://www.justsoftwaresolutions.co.uk/threading/multithreading-in-c++0x-part-4-protecting-shared-data.html) 11 | - Multithreading in C++0x part 5: Flexible locking with std::unique_lock: http://www.justsoftwaresolutions.co.uk/threading/multithreading-in-c++0x-part-5-flexible-locking.html 12 | - Multithreading in C++0x part 6: Lazy initialization and double-checked locking with atomics: [http://www.justsoftwaresolutions.co.uk/threading/multithreading-in-c++0x-part-6-double-checked-locking.html](http://www.justsoftwaresolutions.co.uk/threading/multithreading-in-c++0x-part-6-double-checked-locking.html) 13 | - Multithreading in C++0x part 7: Locking multiple mutexes without deadlock: [http://www.justsoftwaresolutions.co.uk/threading/multithreading-in-c++0x-part-7-locking-multiple-mutexes.html](http://www.justsoftwaresolutions.co.uk/threading/multithreading-in-c++0x-part-7-locking-multiple-mutexes.html) 14 | - Recursive Lock (Mutex) vs Non-Recursive Lock (Mutex): [http://stackoverflow.com/questions/187761/recursive-lock-mutex-vs-non-recursive-lock-mutex](http://stackoverflow.com/questions/187761/recursive-lock-mutex-vs-non-recursive-lock-mutex) 15 | - Handling mutexes in C++: [http://home.roadrunner.com/~hinnant/mutexes/locking.html](http://home.roadrunner.com/~hinnant/mutexes/locking.html) 16 | - Enforcing Correct Mutex Usage with Synchronized Values: [http://www.drdobbs.com/cpp/enforcing-correct-mutex-usage-with-synch/225200269](http://www.drdobbs.com/cpp/enforcing-correct-mutex-usage-with-synch/225200269) 17 | -------------------------------------------------------------------------------- /zh/chapter5-Condition-Variable/5.1 Condition-variable header synopsis.md: -------------------------------------------------------------------------------- 1 | 本小节将介绍 C++11 标准中 `` 头文件里面的类和函数。 2 | 3 | `` 头文件主要包含了与条件变量相关的类和函数。与条件变量相关的类包括 `std::condition_variable` 和 `std::condition_variable_any`,还有枚举类型`std::cv_status`。另外还包括函数 `std::notify_all_at_thread_exit()`。 4 | 5 | ## 5.1 `` 头文件摘要 ## 6 | 7 | namespace std { 8 | class condition_variable; 9 | class condition_variable_any; 10 | void notify_all_at_thread_exit(condition_variable& cond, unique_lock lk); 11 | enum class cv_status { no_timeout, timeout }; 12 | } 13 | 14 | ### 5.1.2 condition_variable 类摘要 ### 15 | 16 | namespace std { 17 | class condition_variable { 18 | public: 19 | condition_variable(); 20 | ~condition_variable(); 21 | condition_variable(const condition_variable&) = delete; 22 | condition_variable& operator=(const condition_variable&) = delete; 23 | 24 | void notify_one() noexcept; 25 | void notify_all() noexcept; 26 | void wait(unique_lock& lock); 27 | template 28 | void wait(unique_lock& lock, Predicate pred); 29 | 30 | template 31 | cv_status wait_until(unique_lock& lock, 32 | const chrono::time_point& abs_time); 33 | 34 | template 35 | bool wait_until(unique_lock& lock, 36 | const chrono::time_point& abs_time, 37 | Predicate pred); 38 | 39 | template 40 | cv_status wait_for(unique_lock& lock, 41 | const chrono::duration& rel_time); 42 | 43 | template 44 | bool wait_for(unique_lock& lock, 45 | const chrono::duration& rel_time, 46 | Predicate pred); 47 | 48 | typedef implementation-defined native_handle_type; 49 | native_handle_type native_handle(); 50 | }; 51 | } 52 | 53 | ### 5.1.3 condition_variable_any 类摘要 ### 54 | 55 | namespace std { 56 | class condition_variable_any { 57 | public: 58 | condition_variable_any(); 59 | ~condition_variable_any(); 60 | condition_variable_any(const condition_variable_any&) = delete; 61 | condition_variable_any& operator=(const condition_variable_any&) = delete; 62 | 63 | void notify_one() noexcept; 64 | void notify_all() noexcept; 65 | template 66 | void wait(Lock& lock); 67 | template 68 | void wait(Lock& lock, Predicate pred); 69 | 70 | template 71 | cv_status wait_until(Lock& lock, const chrono::time_point& abs_time); 72 | 73 | template 74 | bool wait_until(Lock& lock, const chrono::time_point& abs_time, Predicate pred); 75 | 76 | template 77 | cv_status wait_for(Lock& lock, const chrono::duration& rel_time); 78 | 79 | template 80 | bool wait_for(Lock& lock, const chrono::duration& rel_time, Predicate pred); 81 | }; 82 | } -------------------------------------------------------------------------------- /zh/chapter5-Condition-Variable/5.2 Condition-variable-tutorial.md: -------------------------------------------------------------------------------- 1 | ## 5.2 条件变量详解 ## 2 | 3 | ### 5.2.1 `std::condition_variable` 类介绍 ### 4 | 5 | `std::condition_variable` 是条件变量,更多有关条件变量的定义参考维基百科。`Linux` 下使用 `Pthread` 库中的 `pthread_cond_*()` 函数提供了与条件变量相关的功能, `Windows` 则参考 MSDN。 6 | 7 | 当 `std::condition_variable` 对象的某个 `wait` 函数被调用的时候,它使用 `std::unique_lock`(封装 `std::mutex`) 来锁住当前线程。当前线程会一直被阻塞,直到另外一个线程在相同的 `std::condition_variable` 对象上调用了 `notification` 函数来唤醒当前线程。 8 | 9 | `std::condition_variable` 对象通常使用 `std::unique_lock` 来等待,如果需要使用另外的 `lockable` 类型,可以使用 `std::condition_variable_any` 类,本文后面会讲到 `std::condition_variable_any` 的用法。 10 | 11 | 首先我们来看一个简单的[例子](http://www.cplusplus.com/reference/condition_variable/condition_variable/): 12 | 13 | #include // std::cout 14 | #include // std::thread 15 | #include // std::mutex, std::unique_lock 16 | #include // std::condition_variable 17 | 18 | std::mutex mtx; // 全局互斥锁. 19 | std::condition_variable cv; // 全局条件变量. 20 | bool ready = false; // 全局标志位. 21 | 22 | void do_print_id(int id) 23 | { 24 | std::unique_lock lck(mtx); 25 | while (!ready) // 如果标志位不为 true, 则等待... 26 | cv.wait(lck); // 当前线程被阻塞, 当全局标志位变为 true 之后, 27 | // 线程被唤醒, 继续往下执行打印线程编号id. 28 | std::cout << "thread " << id << '\n'; 29 | } 30 | 31 | void go() 32 | { 33 | std::unique_lock lck(mtx); 34 | ready = true; // 设置全局标志位为 true. 35 | cv.notify_all(); // 唤醒所有线程. 36 | } 37 | 38 | int main() 39 | { 40 | std::thread threads[10]; 41 | // spawn 10 threads: 42 | for (int i = 0; i < 10; ++i) 43 | threads[i] = std::thread(do_print_id, i); 44 | 45 | std::cout << "10 threads ready to race...\n"; 46 | go(); // go! 47 | 48 | for (auto & th:threads) 49 | th.join(); 50 | 51 | return 0; 52 | } 53 | 54 | 执行结果如下: 55 | 56 | concurrency ) ./ConditionVariable-basic1 57 | 10 threads ready to race... 58 | thread 1 59 | thread 0 60 | thread 2 61 | thread 3 62 | thread 4 63 | thread 5 64 | thread 6 65 | thread 7 66 | thread 8 67 | thread 9 68 | 69 | 好了,对条件变量有了一个基本的了解之后,我们来看看 std::condition_variable 的各个成员函数。 70 | 71 | #### 5.2.1.1 `std::condition_variable` 构造函数 #### 72 | 73 | 74 | 75 | 76 |
default (1) condition_variable();
copy [deleted] (2) condition_variable (const condition_variable&) = delete;
77 | 78 | 79 | `std::condition_variable` 的拷贝构造函数被禁用,只提供了默认构造函数。 80 | 81 | #### 5.2.1.2 `std::condition_variable::wait()` 介绍 #### 82 | 83 | 84 | 85 | 86 | 87 | 89 |
unconditional (1) void wait (unique_lock<mutex>& lck);
predicate (2)
template <class Predicate>
 88 | void wait (unique_lock<mutex>& lck, Predicate pred); 
90 | 91 | `std::condition_variable` 提供了两种 `wait()` 函数。当前线程调用 `wait()` 后将被阻塞(此时当前线程应该获得了锁(`mutex`),不妨设获得锁 `lck`),直到另外某个线程调用 `notify_*` 唤醒了当前线程。 92 | 93 | 在线程被阻塞时,该函数会自动调用 `lck.unlock()` 释放锁,使得其他被阻塞在锁竞争上的线程得以继续执行。另外,一旦当前线程获得通知(`notified`,通常是另外某个线程调用 `notify_*` 唤醒了当前线程),`wait()` 函数也是自动调用 `lck.lock()`,使得 `lck` 的状态和 `wait` 函数被调用时相同。 94 | 95 | 在第二种情况下(即设置了 `Predicate`),只有当 `pred` 条件为 `false` 时调用 `wait()` 才会阻塞当前线程,并且在收到其他线程的通知后只有当 `pred` 为 `true` 时才会被解除阻塞。因此第二种情况类似以下代码: 96 | 97 | while (!pred()) wait(lck); 98 | 99 | 请看下面例子([参考](http://www.cplusplus.com/reference/condition_variable/condition_variable/wait/)): 100 | 101 | #include // std::cout 102 | #include // std::thread, std::this_thread::yield 103 | #include // std::mutex, std::unique_lock 104 | #include // std::condition_variable 105 | 106 | std::mutex mtx; 107 | std::condition_variable cv; 108 | 109 | int cargo = 0; 110 | bool shipment_available() 111 | { 112 | return cargo != 0; 113 | } 114 | 115 | // 消费者线程. 116 | void consume(int n) 117 | { 118 | for (int i = 0; i < n; ++i) { 119 | std::unique_lock lck(mtx); 120 | cv.wait(lck, shipment_available); 121 | std::cout << cargo << '\n'; 122 | cargo = 0; 123 | } 124 | } 125 | 126 | int main() 127 | { 128 | std::thread consumer_thread(consume, 10); // 消费者线程. 129 | 130 | // 主线程为生产者线程, 生产 10 个物品. 131 | for (int i = 0; i < 10; ++i) { 132 | while (shipment_available()) 133 | std::this_thread::yield(); 134 | std::unique_lock lck(mtx); 135 | cargo = i + 1; 136 | cv.notify_one(); 137 | } 138 | 139 | consumer_thread.join(); 140 | 141 | return 0; 142 | } 143 | 144 | 程序执行结果如下: 145 | 146 | concurrency ) ./ConditionVariable-wait 147 | 1 148 | 2 149 | 3 150 | 4 151 | 5 152 | 6 153 | 7 154 | 8 155 | 9 156 | 10 157 | 158 | #### 5.2.1.3 `std::condition_variable::wait_for()` 介绍 #### 159 | 160 | 161 | 162 | 168 | 169 | 170 | 175 | 176 |
unconditional (1) 163 |
template <class Rep, class Period>
164 | cv_status wait_for (unique_lock<mutex>& lck,
165 |                       const chrono::duration<Rep,Period>& rel_time);
166 | 
167 |
predicate (2) 171 |
template <class Rep, class Period, class Predicate>
172 | bool wait_for (unique_lock<mutex>& lck,
173 |                const chrono::duration<Rep, Period>& rel_time, Predicate pred);
174 |
177 | 178 | 与 `std::condition_variable::wait()` 类似,不过 `wait_for` 可以指定一个时间段,在当前线程收到通知或者指定的时间 `rel_time` 超时之前,该线程都会处于阻塞状态。而一旦超时或者收到了其他线程的通知,`wait_for` 返回,剩下的处理步骤和 `wait()` 类似。 179 | 180 | 另外,`wait_for` 的重载版本(`predicte(2)`)的最后一个参数 pred 表示 `wait_for` 的预测条件,只有当 `pred` 条件为 `false` 时调用 `wait()` 才会阻塞当前线程,并且在收到其他线程的通知后只有当 `pred` 为 `true` 时才会被解除阻塞,因此相当于如下代码: 181 | 182 | return wait_until (lck, chrono::steady_clock::now() + rel_time, std::move(pred)); 183 | 184 | 请看下面的例子([参考](http://www.cplusplus.com/reference/condition_variable/condition_variable/wait_for/)),下面的例子中,主线程等待 `th` 线程输入一个值,然后将 `th` 线程从终端接收的值打印出来,在 `th` 线程接受到值之前,主线程一直等待,每个一秒超时一次,并打印一个 "`.`": 185 | 186 | #include // std::cout 187 | #include // std::thread 188 | #include // std::chrono::seconds 189 | #include // std::mutex, std::unique_lock 190 | #include // std::condition_variable, std::cv_status 191 | 192 | std::condition_variable cv; 193 | 194 | int value; 195 | 196 | void do_read_value() 197 | { 198 | std::cin >> value; 199 | cv.notify_one(); 200 | } 201 | 202 | int main () 203 | { 204 | std::cout << "Please, enter an integer (I'll be printing dots): \n"; 205 | std::thread th(do_read_value); 206 | 207 | std::mutex mtx; 208 | std::unique_lock lck(mtx); 209 | while (cv.wait_for(lck,std::chrono::seconds(1)) == std::cv_status::timeout) { 210 | std::cout << '.'; 211 | std::cout.flush(); 212 | } 213 | 214 | std::cout << "You entered: " << value << '\n'; 215 | 216 | th.join(); 217 | return 0; 218 | } 219 | 220 | #### 5.2.1.4 `std::condition_variable::wait_until` 介绍 #### 221 | 222 | 223 | 224 | 230 | 231 | 232 | 238 | 239 |
unconditional (1) 225 |
template <class Clock, class Duration>
226 | cv_status wait_until (unique_lock<mutex>& lck,
227 |                         const chrono::time_point<Clock,Duration>& abs_time);
228 | 
229 |
predicate (2) 233 |
template <class Clock, class Duration, class Predicate>
234 | bool wait_until (unique_lock<mutex>& lck,
235 |                  const chrono::time_point<Clock,Duration>& abs_time,
236 |                  Predicate pred);
237 |
240 | 241 | 与 `std::condition_variable::wait_for` 类似,但是 `wait_until` 可以指定一个时间点,在当前线程收到通知或者指定的时间点 `abs_time` 超时之前,该线程都会处于阻塞状态。而一旦超时或者收到了其他线程的通知,`wait_until` 返回,剩下的处理步骤和 `wait_until()` 类似。 242 | 243 | 另外,`wait_until` 的重载版本(`predicte`(2))的最后一个参数 `pred` 表示 `wait_until` 的预测条件,只有当 `pred` 条件为 `false` 时调用 `wait()` 才会阻塞当前线程,并且在收到其他线程的通知后只有当 `pred` 为 `true` 时才会被解除阻塞,因此相当于如下代码: 244 | 245 | while (!pred()) 246 | if ( wait_until(lck,abs_time) == cv_status::timeout) 247 | return pred(); 248 | return true; 249 | 250 | ### `std::condition_variable::notify_one(`) 介绍 ### 251 | 252 | 唤醒某个等待(`wait`)线程。如果当前没有等待线程,则该函数什么也不做,如果同时存在多个等待线程,则唤醒某个线程是不确定的(`unspecified`)。 253 | 254 | 请看下例([参考](http://www.cplusplus.com/reference/condition_variable/condition_variable/notify_one/)): 255 | 256 | #include // std::cout 257 | #include // std::thread 258 | #include // std::mutex, std::unique_lock 259 | #include // std::condition_variable 260 | 261 | std::mutex mtx; 262 | std::condition_variable cv; 263 | 264 | int cargo = 0; // shared value by producers and consumers 265 | 266 | void consumer() 267 | { 268 | std::unique_lock < std::mutex > lck(mtx); 269 | while (cargo == 0) 270 | cv.wait(lck); 271 | std::cout << cargo << '\n'; 272 | cargo = 0; 273 | } 274 | 275 | void producer(int id) 276 | { 277 | std::unique_lock < std::mutex > lck(mtx); 278 | cargo = id; 279 | cv.notify_one(); 280 | } 281 | 282 | int main() 283 | { 284 | std::thread consumers[10], producers[10]; 285 | 286 | // spawn 10 consumers and 10 producers: 287 | for (int i = 0; i < 10; ++i) { 288 | consumers[i] = std::thread(consumer); 289 | producers[i] = std::thread(producer, i + 1); 290 | } 291 | 292 | // join them back: 293 | for (int i = 0; i < 10; ++i) { 294 | producers[i].join(); 295 | consumers[i].join(); 296 | } 297 | 298 | return 0; 299 | } 300 | 301 | #### 5.2.1.5 `std::condition_variable::notify_all()` 介绍 #### 302 | 303 | 唤醒所有的等待(`wait`)线程。如果当前没有等待线程,则该函数什么也不做。请看下面的例子: 304 | 305 | #include // std::cout 306 | #include // std::thread 307 | #include // std::mutex, std::unique_lock 308 | #include // std::condition_variable 309 | 310 | std::mutex mtx; // 全局互斥锁. 311 | std::condition_variable cv; // 全局条件变量. 312 | bool ready = false; // 全局标志位. 313 | 314 | void do_print_id(int id) 315 | { 316 | std::unique_lock lck(mtx); 317 | while (!ready) // 如果标志位不为 true, 则等待... 318 | cv.wait(lck); // 当前线程被阻塞, 当全局标志位变为 true 之后, 319 | // 线程被唤醒, 继续往下执行打印线程编号id. 320 | std::cout << "thread " << id << '\n'; 321 | } 322 | 323 | void go() 324 | { 325 | std::unique_lock lck(mtx); 326 | ready = true; // 设置全局标志位为 true. 327 | cv.notify_all(); // 唤醒所有线程. 328 | } 329 | 330 | int main() 331 | { 332 | std::thread threads[10]; 333 | // spawn 10 threads: 334 | for (int i = 0; i < 10; ++i) 335 | threads[i] = std::thread(do_print_id, i); 336 | 337 | std::cout << "10 threads ready to race...\n"; 338 | go(); // go! 339 | 340 | for (auto & th:threads) 341 | th.join(); 342 | 343 | return 0; 344 | } 345 | 346 | ### 5.2.2 `std::condition_variable_any` 介绍 ### 347 | 348 | 与 `std::condition_variable` 类似,只不过 `std::condition_variable_any` 的 `wait` 函数可以接受任何 `lockable` 参数,而 `std::condition_variable` 只能接受 `std::unique_lock` 类型的参数,除此以外,和 `std::condition_variable` 几乎完全一样。 349 | 350 | ### 5.2.3 `std::cv_status` 枚举类型介绍 ### 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 |
cv_status::no_timeoutwait_for 或者 wait_until 没有超时,即在规定的时间段内线程收到了通知。
cv_status::timeoutwait_for 或者 wait_until 超时。
362 | -------------------------------------------------------------------------------- /zh/chapter5-Condition-Variable/5.3 Auxiliary-function.md: -------------------------------------------------------------------------------- 1 | ## 5.3 `std::notify_all_at_thread_exit` ## 2 | 3 | 函数原型为: 4 | 5 | void notify_all_at_thread_exit(condition_variable& cond, unique_lock lck); 6 | 7 | 当调用该函数的线程退出时,所有在 cond 条件变量上等待的线程都会收到通知。请看下例([参考](http://www.cplusplus.com/reference/condition_variable/notify_all_at_thread_exit/)): 8 | 9 | #include // std::cout 10 | #include // std::thread 11 | #include // std::mutex, std::unique_lock 12 | #include // std::condition_variable 13 | 14 | std::mutex mtx; 15 | std::condition_variable cv; 16 | bool ready = false; 17 | 18 | void print_id (int id) { 19 | std::unique_lock lck(mtx); 20 | while (!ready) cv.wait(lck); 21 | // ... 22 | std::cout << "thread " << id << '\n'; 23 | } 24 | 25 | void go() { 26 | std::unique_lock lck(mtx); 27 | std::notify_all_at_thread_exit(cv,std::move(lck)); 28 | ready = true; 29 | } 30 | 31 | int main () 32 | { 33 | std::thread threads[10]; 34 | // spawn 10 threads: 35 | for (int i=0; i<10; ++i) 36 | threads[i] = std::thread(print_id,i); 37 | std::cout << "10 threads ready to race...\n"; 38 | 39 | std::thread(go).detach(); // go! 40 | 41 | for (auto& th : threads) th.join(); 42 | 43 | return 0; 44 | } 45 | -------------------------------------------------------------------------------- /zh/chapter5-Condition-Variable/5.4 Thread-synchronization-with-condition-variable.md: -------------------------------------------------------------------------------- 1 | ## 利用条件变量进行线程同步 ## 2 | 3 | 在多线程同步时,条件变量通常和锁(Lock)一起使用。 4 | 5 | 请看下例([参考](http://en.cppreference.com/w/cpp/thread/condition_variable)): 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | std::mutex m; 14 | std::condition_variable cv; 15 | std::string data; 16 | bool ready = false; 17 | bool processed = false; 18 | 19 | void worker_thread() 20 | { 21 | // Wait until main() sends data 22 | { 23 | std::unique_lock lk(m); 24 | cv.wait(lk, []{return ready;}); 25 | } 26 | 27 | std::cout << "Worker thread is processing data\n"; 28 | data += " after processing"; 29 | 30 | // Send data back to main() 31 | { 32 | std::lock_guard lk(m); 33 | processed = true; 34 | std::cout << "Worker thread signals data processing completed\n"; 35 | } 36 | cv.notify_one(); 37 | } 38 | 39 | int main() 40 | { 41 | std::thread worker(worker_thread); 42 | 43 | data = "Example data"; 44 | // send data to the worker thread 45 | { 46 | std::lock_guard lk(m); 47 | ready = true; 48 | std::cout << "main() signals data ready for processing\n"; 49 | } 50 | cv.notify_one(); 51 | 52 | // wait for the worker 53 | { 54 | std::unique_lock lk(m); 55 | cv.wait(lk, []{return processed;}); 56 | } 57 | std::cout << "Back in main(), data = " << data << '\n'; 58 | 59 | worker.join(); 60 | } 61 | 62 | 执行结果如下: 63 | 64 | main() signals data ready for processing 65 | Worker thread is processing data 66 | Worker thread signals data processing completed 67 | Back in main(), data = Example data after processing -------------------------------------------------------------------------------- /zh/chapter5-Condition-Variable/5.5 Condition-variable vs pthread.md: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /zh/chapter5-Condition-Variable/README.md: -------------------------------------------------------------------------------- 1 | # 本章导读 # 2 | 3 | 本章主要内容如下: 4 | 5 | - `` 头文件中的类及相关函数的介绍。 6 | - `std::condition_variable` 与 Pthread 线程库中的 `pthread_cond_t` 类型的对比,包括两者编程接口的差异及其他细节。 7 | - 利用条件变量 `std::condition_variable` 进行线程同步的应用实例。 8 | 9 | # 第五章 条件变量与线程同步 # 10 | 11 | ## 5.1 [`` 头文件摘要](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter5-Condition-Variable/5.1%20Condition-variable%20header%20synopsis.md) ## 12 | 13 | ## [5.2 条件变量详解](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter5-Condition-Variable/5.2%20Condition-variable-tutorial.md) ## 14 | 15 | ## [5.3 辅助函数介绍](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter5-Condition-Variable/5.3%20Auxiliary-function.md) ## 16 | 17 | ## 5.4 利用条件变量(std::condition_variable)进行线程同步 ## 18 | 19 | ## 5.5 `std::condition_variable` 与 Pthread 条件变量对比 ## 20 | 21 | ## [5.6 资料汇](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter5-Condition-Variable/web-resources.md) ## -------------------------------------------------------------------------------- /zh/chapter5-Condition-Variable/web-resources.md: -------------------------------------------------------------------------------- 1 | # C++ 条件变量与线程同步资料汇 # 2 | 3 | - C++11 - the new ISO C++ standard: [http://www.stroustrup.com/C++11FAQ.html](http://www.stroustrup.com/C++11FAQ.html) 4 | - [http://en.cppreference.com/w/cpp/thread/condition_variable](http://en.cppreference.com/w/cpp/thread/condition_variable) 5 | - C++11 threads, locks and condition variables: [http://www.codeproject.com/Articles/598695/Cplusplus11-threads-locks-and-condition-variables](http://www.codeproject.com/Articles/598695/Cplusplus11-threads-locks-and-condition-variables) 6 | - C++11 Concurrency Tutorial – Part 3: Advanced locking and condition variables: [http://www.baptiste-wicht.com/2012/04/c11-concurrency-tutorial-advanced-locking-and-condition-variables/](http://www.baptiste-wicht.com/2012/04/c11-concurrency-tutorial-advanced-locking-and-condition-variables/) 7 | - C++11 concurrency: condition variables: [http://codexpert.ro/blog/2013/03/01/cpp11-concurrency-condition-variables/](http://codexpert.ro/blog/2013/03/01/cpp11-concurrency-condition-variables/) 8 | - Synchronizing with a condition_variable: [http://thisthread.blogspot.com/2011/09/synchronizing-with-conditionvariable.html](http://thisthread.blogspot.com/2011/09/synchronizing-with-conditionvariable.html) -------------------------------------------------------------------------------- /zh/chapter6-Future/6.1 Future-header-synopsis.md: -------------------------------------------------------------------------------- 1 | 本小节主要介绍 C++11标准 `` 头文件中类和函数的摘要。 2 | 3 | C++11 标准中与异步任务系相关的类型主要是以下四种 `std::promise`,`std::packaged_task`(`std::promise`,`std::packaged_task` 也称为异步任务的提供者 Provider,此外 `std::async` 也可以作为异步任务的提供者,不过 `std::async` 并不是类,而是函数,本章后面会详细介绍异步任务的提供者 Provider),`std::future`,`std::shared_future`。另外 `` 中还定义一些辅助的类,例如: `std::future_error`,`std::future_errc`,`std::status`,`std::launch`。 4 | 5 | 下面我们看看 C++11 标准中是怎么声明以上类型和函数的。 6 | 7 | ## 6.1 `` 头文件摘要 ## 8 | 9 | ### 6.1.1 `` 头文件摘要 ### 10 | namespace std { 11 | enum class future_errc { 12 | broken_promise, 13 | future_already_retrieved, 14 | promise_already_satisfied, 15 | no_state 16 | }; 17 | 18 | enum class launch : unspecified { 19 | async = unspecified, 20 | deferred = unspecified, 21 | implementation-defined 22 | }; 23 | 24 | enum class future_status { 25 | ready, 26 | timeout, 27 | deferred 28 | }; 29 | 30 | template <> struct is_error_code_enum : public true_type { }; 31 | error_code make_error_code(future_errc e); 32 | error_condition make_error_condition(future_errc e); 33 | 34 | const error_category& future_category(); 35 | 36 | class future_error; 37 | 38 | template class promise; 39 | template class promise; 40 | template <> class promise; 41 | 42 | template 43 | void swap(promise& x, promise& y); 44 | 45 | template 46 | struct uses_allocator, Alloc>; 47 | 48 | template class future; 49 | template class future; 50 | template <> class future; 51 | 52 | template class shared_future; 53 | template class shared_future; 54 | template <> class shared_future; 55 | 56 | template class packaged_task; // undefined 57 | template 58 | class packaged_task; 59 | 60 | template 61 | void swap(packaged_task&); 62 | 63 | template 64 | struct uses_allocator, Alloc>; 65 | 66 | template 67 | future::type> 68 | async(F&& f, Args&&... args); 69 | 70 | template 71 | future::type> 72 | async(launch policy, F&& f, Args&&... args); 73 | } 74 | 75 | ### 6.1.2 `std::future_error` 类摘要 ### 76 | 77 | namespace std { 78 | class future_error : public logic_error { 79 | public: 80 | future_error(error_code ec); // exposition only 81 | const error_code& code() const noexcept; 82 | const char* what() const noexcept; 83 | }; 84 | } 85 | 86 | ### 6.1.3 `std::promise` 类摘要 ### 87 | 88 | namespace std { 89 | template 90 | class promise { 91 | public: 92 | promise(); 93 | template 94 | promise(allocator_arg_t, const Allocator& a); 95 | promise(promise&& rhs) noexcept; 96 | promise(const promise& rhs) = delete; 97 | ~promise(); 98 | 99 | // assignment 100 | promise& operator=(promise&& rhs) noexcept; 101 | promise& operator=(const promise& rhs) = delete; 102 | 103 | void swap(promise& other) noexcept; 104 | 105 | // retrieving the result 106 | future get_future(); 107 | 108 | // setting the result 109 | void set_value(see below); 110 | void set_exception(exception_ptr p); 111 | 112 | // setting the result with deferred notification 113 | void set_value_at_thread_exit(const R& r); 114 | void set_value_at_thread_exit(see below); 115 | void set_exception_at_thread_exit(exception_ptr p); 116 | }; 117 | template 118 | void swap(promise& x, promise& y); 119 | template 120 | struct uses_allocator, Alloc>; 121 | } 122 | 123 | ### 6.1.4 `std::future` 类摘要 ### 124 | 125 | namespace std { 126 | template 127 | class future { 128 | public: 129 | future(); 130 | future(future &&); 131 | future(const future& rhs) = delete; 132 | ~future(); 133 | 134 | future& operator=(const future& rhs) = delete; 135 | future& operator=(future&&) noexcept; 136 | shared_future share() &&; 137 | 138 | // retrieving the value 139 | see below get(); 140 | 141 | // functions to check state 142 | bool valid() const; 143 | void wait() const; 144 | template 145 | future_status wait_for(const chrono::duration& rel_time) const; 146 | template 147 | future_status wait_until(const chrono::time_point& abs_time) const; 148 | }; 149 | } 150 | 151 | ### 6.1.5 `std::shared_future` 类摘要 ### 152 | 153 | namespace std { 154 | template 155 | class shared_future { 156 | public: 157 | shared_future() noexcept; 158 | shared_future(const shared_future& rhs); 159 | shared_future(future&&) noexcept; 160 | shared_future(shared_future&& rhs) noexcept; 161 | ~shared_future(); 162 | 163 | shared_future& operator=(const shared_future& rhs); 164 | shared_future& operator=(shared_future&& rhs); 165 | 166 | // retrieving the value 167 | see below get() const; 168 | 169 | // functions to check state 170 | bool valid() const; 171 | void wait() const; 172 | template 173 | future_status wait_for(const chrono::duration& rel_time) const; 174 | template 175 | future_status wait_until(const chrono::time_point& abs_time) const; 176 | }; 177 | } 178 | 179 | ### 6.1.6 `std::async` 函数摘要 ### 180 | 181 | namespace std { 182 | template 183 | future::type> 184 | async(F&& f, Args&&... args); 185 | 186 | template 187 | future::type> 188 | async(launch policy, F&& f, Args&&... args); 189 | } 190 | 191 | ### 6.1.7 `std::packaged_task` 类摘要 ### 192 | 193 | namespace std { 194 | template class packaged_task; // undefined 195 | 196 | template 197 | class packaged_task { 198 | public: 199 | typedef R result_type; 200 | 201 | // construction and destruction 202 | packaged_task() noexcept; 203 | template 204 | explicit packaged_task(F f); 205 | template 206 | explicit packaged_task(allocator_arg_t, const Allocator& a, F f); 207 | explicit packaged_task(R(*f)(ArgTypes...)); 208 | template 209 | explicit packaged_task(F&& f); 210 | template 211 | explicit packaged_task(allocator_arg_t, const Allocator& a, F&& f); 212 | ~packaged_task(); 213 | 214 | // no copy 215 | packaged_task(packaged_task&) = delete; 216 | packaged_task& operator=(packaged_task&) = delete; 217 | 218 | // move support 219 | packaged_task(packaged_task&& other) noexcept; 220 | packaged_task& operator=(packaged_task&& other); 221 | void swap(packaged_task& other) noexcept; 222 | bool valid() const noexcept; 223 | 224 | // result retrieval 225 | future get_future(); 226 | 227 | // execution 228 | void operator()(ArgTypes... ); 229 | void make_ready_at_thread_exit(ArgTypes...); 230 | void reset(); 231 | }; 232 | 233 | template 234 | void swap(packaged_task& x, packaged_task& y) noexcept; 235 | template 236 | struct uses_allocator, Alloc>; 237 | } -------------------------------------------------------------------------------- /zh/chapter6-Future/6.2 Providers-tutorial.md: -------------------------------------------------------------------------------- 1 | `` 头文件中包含了以下几个类和函数: 2 | 3 | 1. Providers 类:`std::promise`, `std::package_task` 4 | 2. Futures 类:`std::future`, `std::shared_future`. 5 | 3. Providers 函数:`std::async()` 6 | 4. 其他类型:`std::future_error`, `std::future_errc`, `std::future_status`, `std::launch`. 7 | 8 | ## 6.2 异步任务提供者(Provider) 介绍 ## 9 | 10 | ### 6.2.1 `std::promise` 类概述 ### 11 | 12 | Promise 对象可以保存某一类型 T 的值,该值可被 future 对象读取(可能在另外一个线程中),因此 promise 也提供了一种线程同步的手段。在 promise 对象构造时可以和一个共享状态(通常是std::future)相关联,并可以在相关联的共享状态(`std::future`)上保存一个类型为 T 的值。 13 | 14 | 可以通过 `get_future` 来获取与该 promise 对象相关联的 future 对象,调用该函数之后,两个对象共享相同的共享状态(shared state) 15 | 16 | 1. promise 对象是异步 Provider,它可以在某一时刻设置共享状态的值。 17 | 2. future 对象可以异步返回共享状态的值,或者在必要的情况下阻塞调用者并等待共享状态标志变为 ready,然后才能获取共享状态的值。 18 | 19 | 下面以一个简单的例子来说明上述关系: 20 | 21 | #include // std::cout 22 | #include // std::ref 23 | #include // std::thread 24 | #include // std::promise, std::future 25 | 26 | void print_int(std::future& fut) { 27 | int x = fut.get(); // 获取共享状态的值. 28 | std::cout << "value: " << x << '\n'; // 打印 value: 10. 29 | } 30 | 31 | int main () 32 | { 33 | std::promise prom; // 生成一个 std::promise 对象. 34 | std::future fut = prom.get_future(); // 和 future 关联. 35 | std::thread t(print_int, std::ref(fut)); // 将 future 交给另外一个线程t. 36 | prom.set_value(10); // 设置共享状态的值, 此处和线程t保持同步. 37 | t.join(); 38 | return 0; 39 | } 40 | 41 | ### 6.2.2 `std::promise` 构造函数 ### 42 | 43 | 44 | 45 | 46 | 47 | 48 | 50 | 51 | 52 | 54 | 55 | 56 | 59 | 60 |
default (1)promise();
with allocator (2)template <class Alloc> promise (allocator_arg_t aa, const Alloc& alloc); 49 |
copy [deleted] (3)promise (const promise&) = delete; 53 |
move (4) 57 | promise (promise&& x) noexcept; 58 |
61 | 62 | 63 | 64 | 1. 默认构造函数,初始化一个空的共享状态。 65 | 2. 带自定义内存分配器的构造函数,与默认构造函数类似,但是使用自定义分配器来分配共享状态。 66 | 3. 拷贝构造函数,被禁用。 67 | 4. 移动构造函数。 68 | 69 | 另外,`std::promise` 的 `operator=` 没有拷贝语义,即 `std::promise` 普通的赋值操作被禁用,`operator=` 只有 move 语义,所以 `std::promise` 对象是禁止拷贝的。 70 | 71 | 例子: 72 | 73 | #include // std::cout 74 | #include // std::thread 75 | #include // std::promise, std::future 76 | 77 | std::promise prom; 78 | 79 | void print_global_promise () { 80 | std::future fut = prom.get_future(); 81 | int x = fut.get(); 82 | std::cout << "value: " << x << '\n'; 83 | } 84 | 85 | int main () 86 | { 87 | std::thread th1(print_global_promise); 88 | prom.set_value(10); 89 | th1.join(); 90 | 91 | prom = std::promise(); // prom 被move赋值为一个新的 promise 对象. 92 | 93 | std::thread th2 (print_global_promise); 94 | prom.set_value (20); 95 | th2.join(); 96 | 97 | return 0; 98 | } 99 | 100 | ### 6.2.3 `std::promise::get_future` 介绍 ### 101 | 102 | 该函数返回一个与 promise 共享状态相关联的 future 。返回的 future 对象可以访问由 promise 对象设置在共享状态上的值或者某个异常对象。只能从 promise 共享状态获取一个 future 对象。在调用该函数之后,promise 对象通常会在某个时间点准备好(设置一个值或者一个异常对象),如果不设置值或者异常,promise 对象在析构时会自动地设置一个 `future_error` 异常(`broken_promise`)来设置其自身的准备状态。上面的例子中已经提到了 `get_future`,此处不再重复。 103 | 104 | ### 6.2.4 `std::promise::set_value` 介绍 ### 105 | 106 | 107 | 108 | 113 | 114 | 115 | 119 | 120 |
generic template (1) 109 |
void set_value (const T& val);
110 | void set_value (T&& val);
111 | 
112 |
specializations (2) 116 |
void promise<R&>::set_value (R& val);   // when T is a reference type (R&)
117 | void promise<void>::set_value (void);   // when T is void
118 |
121 | 122 | 设置共享状态的值,此后 promise 的共享状态标志变为 `ready`. 123 | 124 | ### 6.2.5 `std::promise::set_exception` 介绍 ### 125 | 126 | 为 promise 对象设置异常,此后 promise 的共享状态变标志变为 `ready`,例子如下,线程1从终端接收一个整数,线程 2 将该整数打印出来,如果线程 1 接收一个非整数,则为 promise 设置一个异常(failbit) ,线程 2 在 `std::future::get` 是抛出该异常。 127 | 128 | #include // std::cin, std::cout, std::ios 129 | #include // std::ref 130 | #include // std::thread 131 | #include // std::promise, std::future 132 | #include // std::exception, std::current_exception 133 | 134 | void get_int(std::promise& prom) { 135 | int x; 136 | std::cout << "Please, enter an integer value: "; 137 | std::cin.exceptions (std::ios::failbit); // throw on failbit 138 | try { 139 | std::cin >> x; // sets failbit if input is not int 140 | prom.set_value(x); 141 | } catch (std::exception&) { 142 | prom.set_exception(std::current_exception()); 143 | } 144 | } 145 | 146 | void print_int(std::future& fut) { 147 | try { 148 | int x = fut.get(); 149 | std::cout << "value: " << x << '\n'; 150 | } catch (std::exception& e) { 151 | std::cout << "[exception caught: " << e.what() << "]\n"; 152 | } 153 | } 154 | 155 | int main () 156 | { 157 | std::promise prom; 158 | std::future fut = prom.get_future(); 159 | 160 | std::thread th1(get_int, std::ref(prom)); 161 | std::thread th2(print_int, std::ref(fut)); 162 | 163 | th1.join(); 164 | th2.join(); 165 | return 0; 166 | } 167 | 168 | ### 6.2.6 `std::promise::set_value_at_thread_exit` 介绍 ### 169 | 170 | 设置共享状态的值,但是不将共享状态的标志设置为 `ready`,当线程退出时该 promise 对象会自动设置为 ready。如果某个 `std::future` 对象与该 promise 对象的共享状态相关联,并且该 future 对象正在调用 `get`,则调用 get 的线程会被阻塞,当线程退出时,调用 `future::get` 的线程解除阻塞,同时 `get` 返回 `set_value_at_thread_exit` 所设置的值。注意,该函数已经设置了 promise 共享状态的值,如果在线程结束之前有其他设置或者修改共享状态的值的操作,则会抛出 `future_error`( `promise_already_satisfied` )。 171 | 172 | 173 | ### 6.2.7 `std::promise::swap` 介绍 ### 174 | 175 | 交换 promise 的共享状态。 -------------------------------------------------------------------------------- /zh/chapter6-Future/6.3 Providers-tutorial-2.md: -------------------------------------------------------------------------------- 1 | ## 6.3 异步任务提供者(Provider) 介绍(续) ## 2 | 3 | 本文主要介绍 `std::packaged_task`。 4 | 5 | `std::packaged_task` 包装一个可调用的对象,并且允许异步获取该可调用对象产生的结果,从包装可调用对象意义上来讲,`std::packaged_task` 与 `std::function` 类似,只不过 `std::packaged_task` 将其包装的可调用对象的执行结果传递给一个 `std::future` 对象(该对象通常在另外一个线程中获取 `std::packaged_task` 任务的执行结果)。 6 | 7 | `std::packaged_task` 对象内部包含了两个最基本元素,一、被包装的任务(stored task),任务(task)是一个可调用的对象,如函数指针、成员函数指针或者函数对象,二、共享状态(shared state),用于保存任务的返回值,可以通过 `std::future` 对象来达到异步访问共享状态的效果。 8 | 9 | 可以通过 `std::packged_task::get_future` 来获取与共享状态相关联的 `std::future` 对象。在调用该函数之后,两个对象共享相同的共享状态,具体解释如下: 10 | 11 | 1. `std::packaged_task` 对象是异步 Provider,它在某一时刻通过调用被包装的任务来设置共享状态的值。 12 | 2. `std::future` 对象是一个异步返回对象,通过它可以获得共享状态的值,当然在必要的时候需要等待共享状态标志变为 `ready`. 13 | 14 | `std::packaged_task` 的共享状态的生命周期一直持续到最后一个与之相关联的对象被释放或者销毁为止。下面一个小例子大致讲了 `std::packaged_task` 的用法: 15 | 16 | #include // std::cout 17 | #include // std::packaged_task, std::future 18 | #include // std::chrono::seconds 19 | #include // std::thread, std::this_thread::sleep_for 20 | 21 | // count down taking a second for each value: 22 | int countdown (int from, int to) { 23 | for (int i=from; i!=to; --i) { 24 | std::cout << i << '\n'; 25 | std::this_thread::sleep_for(std::chrono::seconds(1)); 26 | } 27 | std::cout << "Finished!\n"; 28 | return from - to; 29 | } 30 | 31 | int main () 32 | { 33 | std::packaged_task task(countdown); // 设置 packaged_task 34 | std::future ret = task.get_future(); // 获得与 packaged_task 共享状态相关联的 future 对象. 35 | 36 | std::thread th(std::move(task), 10, 0); //创建一个新线程完成计数任务. 37 | 38 | int value = ret.get(); // 等待任务完成并获取结果. 39 | 40 | std::cout << "The countdown lasted for " << value << " seconds.\n"; 41 | 42 | th.join(); 43 | return 0; 44 | } 45 | 46 | 执行结果为: 47 | 48 | concurrency ) ./Packaged_Task1 49 | 9 50 | 7 51 | 5 52 | 3 53 | 1 54 | Finished! 55 | The countdown lasted for 10 seconds. 56 | 57 | ### 6.3.1 `std::packaged_task` 构造函数 ### 58 | 59 | 60 | 61 | 63 | 64 | 65 | 70 | 71 | 72 | 77 | 78 | 79 | 81 | 82 | 83 | 85 | 86 |
default (1)packaged_task() noexcept; 62 |
initialization (2) 66 |
template <class Fn>
 67 | explicit packaged_task (Fn&& fn);
 68 | 
69 |
with allocator (3) 73 |
template <class Fn, class Alloc>
 74 | explicit packaged_task (allocator_arg_t aa, const Alloc& alloc, Fn&& fn);
 75 | 
76 |
copy [deleted] (4)packaged_task (const packaged_task&) = delete; 80 |
move (5)packaged_task (packaged_task&& x) noexcept; 84 |
87 | 88 | `std::packaged_task` 构造函数共有 5 中形式,不过拷贝构造已经被禁用了。下面简单地介绍一下上述几种构造函数的语义: 89 | 90 | 1. 默认构造函数,初始化一个空的共享状态,并且该 `packaged_task` 对象无包装任务。 91 | 2. 初始化一个共享状态,并且被包装任务由参数 fn 指定。 92 | 3. 带自定义内存分配器的构造函数,与默认构造函数类似,但是使用自定义分配器来分配共享状态。 93 | 4. 拷贝构造函数,被禁用。 94 | 5. 移动构造函数。 95 | 96 | 下面例子介绍了各类构造函数的用法: 97 | 98 | #include // std::cout 99 | #include // std::move 100 | #include // std::packaged_task, std::future 101 | #include // std::thread 102 | 103 | int main () 104 | { 105 | std::packaged_task foo; // 默认构造函数. 106 | 107 | // 使用 lambda 表达式初始化一个 packaged_task 对象. 108 | std::packaged_task bar([](int x){return x*2;}); 109 | 110 | foo = std::move(bar); // move-赋值操作,也是 C++11 中的新特性. 111 | 112 | // 获取与 packaged_task 共享状态相关联的 future 对象. 113 | std::future ret = foo.get_future(); 114 | 115 | std::thread(std::move(foo), 10).detach(); // 产生线程,调用被包装的任务. 116 | 117 | int value = ret.get(); // 等待任务完成并获取结果. 118 | std::cout << "The double of 10 is " << value << ".\n"; 119 | 120 | return 0; 121 | } 122 | 123 | 与 `std::promise` 类似, `std::packaged_task` 也禁用了普通的赋值操作运算,只允许 `move` 赋值运算。 124 | 125 | 126 | ### 6.3.2 `std::packaged_task::valid` 介绍 ### 127 | 128 | 检查当前 `packaged_task` 是否和一个有效的共享状态相关联,对于由默认构造函数生成的 `packaged_task` 对象,该函数返回 `false`,除非中间进行了 `move` 赋值操作或者 `swap` 操作。 129 | 130 | 请看下例: 131 | 132 | #include // std::cout 133 | #include // std::move 134 | #include // std::packaged_task, std::future 135 | #include // std::thread 136 | 137 | // 在新线程中启动一个 int(int) packaged_task. 138 | std::future launcher(std::packaged_task& tsk, int arg) 139 | { 140 | if (tsk.valid()) { 141 | std::future ret = tsk.get_future(); 142 | std::thread (std::move(tsk),arg).detach(); 143 | return ret; 144 | } 145 | else return std::future(); 146 | } 147 | 148 | int main () 149 | { 150 | std::packaged_task tsk([](int x){return x*2;}); 151 | 152 | std::future fut = launcher(tsk,25); 153 | 154 | std::cout << "The double of 25 is " << fut.get() << ".\n"; 155 | 156 | return 0; 157 | } 158 | 159 | 160 | ### 6.3.3 `std::packaged_task::get_future` 介绍 ### 161 | 162 | 返回一个与 `packaged_task` 对象共享状态相关的 `future` 对象。返回的 `future` 对象可以获得由另外一个线程在该 `packaged_task` 对象的共享状态上设置的某个值或者异常。 163 | 164 | 请看例子(其实前面已经讲了 `get_future` 的例子): 165 | 166 | #include // std::cout 167 | #include // std::move 168 | #include // std::packaged_task, std::future 169 | #include // std::thread 170 | 171 | int main () 172 | { 173 | std::packaged_task tsk([](int x) { return x * 3; })); // package task 174 | 175 | std::future fut = tsk.get_future(); // 获取 future 对象. 176 | 177 | std::thread(std::move(tsk), 100).detach(); // 生成新线程并调用packaged_task. 178 | 179 | int value = fut.get(); // 等待任务完成, 并获取结果. 180 | 181 | std::cout << "The triple of 100 is " << value << ".\n"; 182 | 183 | return 0; 184 | } 185 | 186 | ### 6.3.4 `std::packaged_task::operator()(Args... args)` 介绍 ### 187 | 188 | 调用该 packaged_task 对象所包装的对象(通常为函数指针,函数对象,lambda 表达式等),传入的参数为 args. 调用该函数一般会发生两种情况: 189 | 190 | 1. 如果成功调用 `packaged_task` 所包装的对象,则返回值(如果被包装的对象有返回值的话)被保存在 `packaged_task` 的共享状态中。 191 | 2. 如果调用 `packaged_task` 所包装的对象失败,并且抛出了异常,则异常也会被保存在 `packaged_task` 的共享状态中。 192 | 193 | 以上两种情况都使共享状态的标志变为 `ready`,因此其他等待该共享状态的线程可以获取共享状态的值或者异常并继续执行下去。 194 | 195 | 共享状态的值可以通过在 future 对象(由 `get_future` 获得)上调用 `get` 来获得。 196 | 197 | 由于被包装的任务在 `packaged_task` 构造时指定,因此调用 `operator()` 的效果由 packaged_task 对象构造时所指定的可调用对象来决定: 198 | 199 | 1. 如果被包装的任务是函数指针或者函数对象,调用 `std::packaged_task::operator()` 只是将参数传递给被包装的对象。 200 | 2. 如果被包装的任务是指向类的非静态成员函数的指针,那么 `std::packaged_task::operator()` 的第一个参数应该指定为成员函数被调用的那个对象,剩余的参数作为该成员函数的参数。 201 | 3. 如果被包装的任务是指向类的非静态成员变量,那么 `std::packaged_task::operator()` 只允许单个参数。 202 | 203 | ### 6.3.5 `std::packaged_task::make_ready_at_thread_exit` 介绍 ### 204 | 205 | 该函数会调用被包装的任务,并向任务传递参数,类似 `std::packaged_task` 的 `operator()` 成员函数。但是与 `operator()` 函数不同的是,`make_ready_at_thread_exit` 并不会立即设置共享状态的标志为 `ready`,而是在线程退出时设置共享状态的标志。 206 | 207 | 如果与该 packaged_task 共享状态相关联的 future 对象在 `future::get` 处等待,则当前的 `future::get` 调用会被阻塞,直到线程退出。而一旦线程退出,`future::get` 调用继续执行,或者抛出异常。 208 | 209 | 注意,该函数已经设置了 promise 共享状态的值,如果在线程结束之前有其他设置或者修改共享状态的值的操作,则会抛出 `future_error`( `promise_already_satisfied` )。 210 | 211 | ### 6.3.6 `std::packaged_task::reset()` 介绍 ### 212 | 213 | 重置 packaged_task 的共享状态,但是保留之前的被包装的任务。请看例子,该例子中,packaged_task 被重用了多次: 214 | 215 | #include // std::cout 216 | #include // std::move 217 | #include // std::packaged_task, std::future 218 | #include // std::thread 219 | 220 | // a simple task: 221 | int triple (int x) { return x*3; } 222 | 223 | int main () 224 | { 225 | std::packaged_task tsk (triple); // package task 226 | std::future fut = tsk.get_future(); 227 | std::thread (std::move(tsk), 100).detach(); 228 | std::cout << "The triple of 100 is " << fut.get() << ".\n"; 229 | 230 | 231 | // re-use same task object: 232 | tsk.reset(); 233 | fut = tsk.get_future(); 234 | std::thread(std::move(tsk), 200).detach(); 235 | std::cout << "Thre triple of 200 is " << fut.get() << ".\n"; 236 | 237 | return 0; 238 | } 239 | 240 | ### 6.3.7 `std::packaged_task::swap()` 介绍 ### 241 | 242 | 交换 `packaged_task` 的共享状态。 243 | 244 | 好了,`std::packaged_task` 介绍到这里,本文参考了 [http://www.cplusplus.com/reference/future/packaged_task/](http://www.cplusplus.com/reference/future/packaged_task/) 相关的内容。下一节我将向大家介绍 `std::future`,`std::shared_future` 以及 `std::future_error`,另外还会介绍 `` 头文件中的 `std::async`,`std::future_category` 函数以及相关枚举类型。 245 | 246 | -------------------------------------------------------------------------------- /zh/chapter6-Future/6.4 Future-tutorial.md: -------------------------------------------------------------------------------- 1 | ## 6.4 Future 类型详解 ## 2 | 3 | 本文主要介绍 `std::future`,`std::shared_future` 以及 `std::future_error`,另外还会介绍 `` 头文件中的 `std::async`,`std::future_category` 函数以及相关枚举类型。 4 | 5 | ### 6.4.1 `std::future` 详解 ### 6 | 7 | ### 6.4.1.1 `std::future` 概述 #### 8 | 9 | 前面已经多次提到过 `std::future`,那么 `std::future` 究竟是什么呢?简单地说,`std::future` 可以用来获取异步任务的结果,因此可以把它当成一种简单的线程间同步的手段。`std::future` 通常由某个 Provider 创建,你可以把 Provider 想象成一个异步任务的提供者,Provider 在某个线程中设置共享状态的值,与该共享状态相关联的 `std::future` 对象调用 `get`(通常在另外一个线程中) 获取该值,如果共享状态的标志不为 `ready`,则调用 `std::future::get` 会阻塞当前的调用者,直到 Provider 设置了共享状态的值(此时共享状态的标志变为 `ready`),`std::future::get` 返回异步任务的值或异常(如果发生了异常)。 10 | 11 | 一个有效(`valid`)的 `std::future` 对象通常由以下三种 Provider 创建,并和某个共享状态相关联。Provider 可以是函数或者类,其实我们前面都已经提到了,他们分别是: 12 | 13 | 1. `std::async` 函数,本文后面会介绍 `std::async()` 函数。 14 | 2. `std::promise::get_future`,`get_future` 为 promise 类的成员函数,详见 C++11 并发指南四(`` 详解一 `std::promise` 介绍)。 15 | 3. `std::packaged_task::get_future`,此时 `get_future` 为 `packaged_task` 的成员函数,详见C++11 并发指南四(`` 详解二 `std::packaged_task` 介绍)。 16 | 17 | 一个 `std::future` 对象只有在有效(`valid`)的情况下才有用(useful),由 `std::future` 默认构造函数创建的 `future` 对象不是有效的(除非当前非有效的 `future` 对象被 `move` 赋值另一个有效的 future 对象)。 18 | 19 | 在一个有效的 `future` 对象上调用 `get` 会阻塞当前的调用者,直到 Provider 设置了共享状态的值或异常(此时共享状态的标志变为 `ready`),`std::future::get` 将返回异步任务的值或异常(如果发生了异常)。 20 | 21 | 下面以一个简单的例子说明上面一段文字吧(参考): 22 | 23 | // future example 24 | #include // std::cout 25 | #include // std::async, std::future 26 | #include // std::chrono::milliseconds 27 | 28 | // a non-optimized way of checking for prime numbers: 29 | bool is_prime(int x) 30 | { 31 | for (int i = 2; i < x; ++i) 32 | if (x % i == 0) 33 | return false; 34 | return true; 35 | } 36 | 37 | int main() 38 | { 39 | // call function asynchronously: 40 | std::future < bool > fut = std::async(is_prime, 444444443); 41 | 42 | // do something while waiting for function to set future: 43 | std::cout << "checking, please wait"; 44 | std::chrono::milliseconds span(100); 45 | while (fut.wait_for(span) == std::future_status::timeout) 46 | std::cout << '.'; 47 | 48 | bool x = fut.get(); // retrieve return value 49 | 50 | std::cout << "\n444444443 " << (x ? "is" : "is not") << " prime.\n"; 51 | 52 | return 0; 53 | } 54 | 55 | 56 | #### 6.4.1.2 `std::future` 构造函数 #### 57 | 58 | `std::future` 一般由 `std::async`, `std::promise::get_future`, `std::packaged_task::get_future` 创建,不过也提供了构造函数,如下表所示: 59 | 60 | 61 | 62 | 63 | 64 | 65 | 67 | 68 | 69 | 70 | 71 |
default (1)future() noexcept;
copy [deleted] (2)future(const future&) = delete; 66 |
move (3)future(future&& x) noexcept;
72 | 73 | 不过 `std::future` 的拷贝构造函数是被禁用的,只提供了默认的构造函数和 `move` 构造函数(注:C++ 新特性)。另外,`std::future` 的普通赋值操作也被禁用,只提供了 `move` 赋值操作。如下代码所示: 74 | 75 | std::future fut; // 默认构造函数 76 | fut = std::async(do_some_task); // move-赋值操作 77 | 78 | #### 6.4.1.3 `std::future::share()` #### 79 | 80 | 返回一个 `std::shared_future` 对象(本文后续内容将介绍 `std::shared_future`),调用该函数之后,该 `std::future` 对象本身已经不和任何共享状 态相关联,因此该 `std::future` 的状态不再是 `valid` 的了。 81 | 82 | #include // std::cout 83 | #include // std::async, std::future, std::shared_future 84 | 85 | int do_get_value() { return 10; } 86 | 87 | int main () 88 | { 89 | std::future fut = std::async(do_get_value); 90 | std::shared_future shared_fut = fut.share(); 91 | 92 | // 共享的 future 对象可以被多次访问. 93 | std::cout << "value: " << shared_fut.get() << '\n'; 94 | std::cout << "its double: " << shared_fut.get()*2 << '\n'; 95 | 96 | return 0; 97 | } 98 | 99 | #### 6.4.1.4 `std::future::get()` #### 100 | 101 | `std::future::get` 一共有三种形式,如下表所示: 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 |
generic template (1)T get();
reference specialization (2)R& future<R&>::get(); // when T is a reference type (R&)
void specialization (3)void future<void>::get(); // when T is void
114 | 115 | 当与该 `std::future` 对象相关联的共享状态标志变为 `ready` 后,调用该函数将返回保存在共享状态中的值,如果共享状态的标志不为 `ready`,则调用该函数会阻塞当前的调用者,而此后一旦共享状态的标志变为 `ready`,`get` 返回 Provider 所设置的共享状态的值或者异常(如果抛出了异常)。 116 | 117 | 请看下面的程序: 118 | 119 | #include // std::cin, std::cout, std::ios 120 | #include // std::ref 121 | #include // std::thread 122 | #include // std::promise, std::future 123 | #include // std::exception, std::current_exception 124 | 125 | void get_int(std::promise& prom) { 126 | int x; 127 | std::cout << "Please, enter an integer value: "; 128 | std::cin.exceptions (std::ios::failbit); // throw on failbit 129 | try { 130 | std::cin >> x; // sets failbit if input is not int 131 | prom.set_value(x); 132 | } catch (std::exception&) { 133 | prom.set_exception(std::current_exception()); 134 | } 135 | } 136 | 137 | void print_int(std::future& fut) { 138 | try { 139 | int x = fut.get(); 140 | std::cout << "value: " << x << '\n'; 141 | } catch (std::exception& e) { 142 | std::cout << "[exception caught: " << e.what() << "]\n"; 143 | } 144 | } 145 | 146 | int main () 147 | { 148 | std::promise prom; 149 | std::future fut = prom.get_future(); 150 | 151 | std::thread th1(get_int, std::ref(prom)); 152 | std::thread th2(print_int, std::ref(fut)); 153 | 154 | th1.join(); 155 | th2.join(); 156 | return 0; 157 | } 158 | 159 | #### 6.4.1.5 `std::future::valid()` #### 160 | 161 | 检查当前的 `std::future` 对象是否有效,即释放与某个共享状态相关联。 162 | 163 | 一个有效的 `std::future` 对象只能通过 `std::async()`, `std::future::get_future` 或者 `std::packaged_task::get_future` 来初始化。 164 | 165 | 另外由 `std::future` 默认构造函数创建的 `std::future` 对象是无效(`invalid`)的,当然通过 `std::future` 的 `move` 赋值后该 `std::future` 对象也可以变为 `valid`。 166 | 167 | #include // std::cout 168 | #include // std::async, std::future 169 | #include // std::move 170 | 171 | int do_get_value() { return 11; } 172 | 173 | int main () 174 | { 175 | // 由默认构造函数创建的 std::future 对象, 176 | // 初始化时该 std::future 对象处于为 invalid 状态. 177 | std::future foo, bar; 178 | foo = std::async(do_get_value); // move 赋值, foo 变为 valid. 179 | bar = std::move(foo); // move 赋值, bar 变为 valid, 而 move 赋值以后 foo 变为 invalid. 180 | 181 | if (foo.valid()) 182 | std::cout << "foo's value: " << foo.get() << '\n'; 183 | else 184 | std::cout << "foo is not valid\n"; 185 | 186 | if (bar.valid()) 187 | std::cout << "bar's value: " << bar.get() << '\n'; 188 | else 189 | std::cout << "bar is not valid\n"; 190 | 191 | return 0; 192 | } 193 | 194 | #### 6.4.1.6 std::future::wait() #### 195 | 196 | 等待与当前 `std::future` 对象相关联的共享状态的标志变为 `ready`. 197 | 198 | 如果共享状态的标志不是 `ready`(此时 Provider 没有在共享状态上设置值(或者异常)),调用该函数会被阻塞当前线程,直到共享状态的标志变为 ready。 199 | 200 | 一旦共享状态的标志变为 `ready`,`wait()` 函数返回,当前线程被解除阻塞,但是 `wait()` 并不读取共享状态的值或者异常。下面的代码说明了 `std::future::wait()` 的用法 201 | 202 | #include // std::cout 203 | #include // std::async, std::future 204 | #include // std::chrono::milliseconds 205 | 206 | // a non-optimized way of checking for prime numbers: 207 | bool do_check_prime(int x) // 为了体现效果, 该函数故意没有优化. 208 | { 209 | for (int i = 2; i < x; ++i) 210 | if (x % i == 0) 211 | return false; 212 | return true; 213 | } 214 | 215 | int main() 216 | { 217 | // call function asynchronously: 218 | std::future < bool > fut = std::async(do_check_prime, 194232491); 219 | 220 | std::cout << "Checking...\n"; 221 | fut.wait(); 222 | 223 | std::cout << "\n194232491 "; 224 | if (fut.get()) // guaranteed to be ready (and not block) after wait returns 225 | std::cout << "is prime.\n"; 226 | else 227 | std::cout << "is not prime.\n"; 228 | 229 | return 0; 230 | } 231 | 232 | 执行结果如下: 233 | 234 | concurrency ) ./Future-wait 235 | Checking... 236 | 237 | 194232491 is prime. 238 | concurrency ) 239 | 240 | #### 6.4.1.7 `std::future::wait_for()` #### 241 | 242 | 与 `std::future::wait()` 的功能类似,即等待与该 `std::future` 对象相关联的共享状态的标志变为 `ready`,该函数原型如下: 243 | 244 | template 245 | future_status wait_for (const chrono::duration& rel_time) const; 246 | 247 | 而与 `std::future::wait()` 不同的是,`wait_for()` 可以设置一个时间段 `rel_time`,如果共享状态的标志在该时间段结束之前没有被 Provider 设置为 `ready`,则调用 `wait_for` 的线程被阻塞,在等待了 `rel_time` 的时间长度后 `wait_for()` 返回,返回值如下: 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 |
返回值描述
future_status::ready共享状态的标志已经变为 ready,即 Provider 在共享状态上设置了值或者异常。
future_status::timeout超时,即在规定的时间内共享状态的标志没有变为 ready。
future_status::deferred共享状态包含一个 deferred 函数。
264 | 265 | 请看下面的例子: 266 | 267 | #include // std::cout 268 | #include // std::async, std::future 269 | #include // std::chrono::milliseconds 270 | 271 | // a non-optimized way of checking for prime numbers: 272 | bool do_check_prime(int x) // 为了体现效果, 该函数故意没有优化. 273 | { 274 | for (int i = 2; i < x; ++i) 275 | if (x % i == 0) 276 | return false; 277 | return true; 278 | } 279 | 280 | int main() 281 | { 282 | // call function asynchronously: 283 | std::future < bool > fut = std::async(do_check_prime, 194232491); 284 | 285 | std::cout << "Checking...\n"; 286 | std::chrono::mi lliseconds span(1000); // 设置超时间隔. 287 | 288 | // 如果超时,则输出".",继续等待 289 | while (fut.wait_for(span) == std::future_status::timeout) 290 | std::cout << '.'; 291 | 292 | std::cout << "\n194232491 "; 293 | if (fut.get()) // guaranteed to be ready (and not block) after wait returns 294 | std::cout << "is prime.\n"; 295 | else 296 | std::cout << "is not prime.\n"; 297 | 298 | return 0; 299 | } 300 | 301 | #### 6.4.1.8 `std::future::wait_until()` #### 302 | 303 | 与 `std::future::wait()` 的功能类似,即等待与该 `std::future` 对象相关联的共享状态的标志变为 `ready`,该函数原型如下: 304 | 305 | template 306 | future_status wait_until (const chrono::time_point& abs_time) const; 307 | 308 | 而与 `std::future::wait()` 不同的是,`wait_until()` 可以设置一个系统绝对时间点 `abs_time`,如果共享状态的标志在该时间点到来之前没有被 Provider 设置为 `ready`,则调用 `wait_until` 的线程被阻塞,在 `abs_time` 这一时刻到来之后 `wait_until()` 返回,返回值如下: 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 |
返回值描述
future_status::ready共享状态的标志已经变为 ready,即 Provider 在共享状态上设置了值或者异常。
future_status::timeout超时,即在规定的时间内共享状态的标志没有变为 ready。
future_status::deferred共享状态包含一个 deferred 函数。
325 | 326 | ### 6.4.2 `std::shared_future` 介绍 ### 327 | 328 | `std::shared_future` 与 `std::future` 类似,但是 `std::shared_future` 可以拷贝、多个 `std::shared_future` 可以共享某个共享状态的最终结果(即共享状态的某个值或者异常)。`shared_future` 可以通过某个 `std::future` 对象隐式转换(参见 `std::shared_future` 的构造函数),或者通过 `std::future::share()` 显示转换,无论哪种转换,被转换的那个 `std::future` 对象都会变为 not-valid. 329 | 330 | #### 6.4.2.1 `std::shared_future` 构造函数 #### 331 | 332 | `std::shared_future` 共有四种构造函数,如下表所示: 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 |
default (1)shared_future() noexcept;
copy (2)shared_future (const shared_future& x);
move (3)shared_future (shared_future&& x) noexcept;
move from future (4)shared_future (future<T>&& x) noexcept;
348 | 349 | 最后 `move from future(4)` 即从一个有效的 `std::future` 对象构造一个 `std::shared_future`,构造之后 `std::future` 对象 x 变为无效(not-valid)。 350 | 351 | #### 6.4.2.2 `std::shared_future` 其他成员函数 #### 352 | 353 | `std::shared_future` 的成员函数和 `std::future` 大部分相同,如下(每个成员函数都给出了连接): 354 | 355 | - `operator=()`: 赋值操作符,与 std::future 的赋值操作不同,std::shared_future 除了支持 move 赋值操作外,还支持普通的赋值操作。 356 | - `get()`: 获取与该 std::shared_future 对象相关联的共享状态的值(或者异常)。 357 | - `valid()`: 有效性检查。 358 | - `wait()`: 等待与该 std::shared_future 对象相关联的共享状态的标志变为 ready。 359 | - `wait_for()`: 等待与该 std::shared_future 对象相关联的共享状态的标志变为 ready。(等待一段时间,超过该时间段wait_for 返回。) 360 | - `wait_until()`: 等待与该 std::shared_future 对象相关联的共享状态的标志变为 ready。(在某一时刻前等待,超过该时刻 wait_until 返回。) 361 | -------------------------------------------------------------------------------- /zh/chapter6-Future/6.5 Auxiliary-types.md: -------------------------------------------------------------------------------- 1 | ## 6.5 与异步任务相关的类型介绍 ## 2 | 3 | ### 6.5.1 `std::future_error` 介绍 ### 4 | 5 | class future_error : public logic_error; 6 | 7 | `std::future_error` 继承子 C++ 标准异常体系中的 `logic_error`,有关 C++ 异常的继承体系,请参考相关的C++教程 ;-)。 8 | 9 | ### 6.5.2 其他与 `std::future` 相关的枚举类介绍 ### 10 | 11 | 下面介绍与 `std::future` 相关的枚举类型。与 `std::future` 相关的枚举类型包括: 12 | 13 | enum class future_errc; 14 | enum class future_status; 15 | enum class launch; 16 | 17 | 下面分别介绍以上三种枚举类型: 18 | 19 | #### 6.5.2.1 `std::future_errc` 类型 #### 20 | 21 | `std::future_errc` 类型描述如下: 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 |
类型
取值
描述
broken_promise0与该 std::future 共享状态相关联的 std::promise 对象在设置值或者异常之前一被销毁。
future_already_retrieved1与该 std::future 对象相关联的共享状态的值已经被当前 Provider 获取了,即调用了 std::future::get 函数。
promise_already_satisfied2std::promise 对象已经对共享状态设置了某一值或者异常。
no_state3无共享状态。
46 | 47 | #### 6.5.2.2 `std::future_status` 类型 #### 48 | 49 | `std::future_status` 类型主要用在 `std::future`(或 `std::shared_future`)中的 `wait_for` 和 `wait_until` 两个函数中的。 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 |
类型取值
描述
future_status::ready0`wait_for`(`或 wait_until`) 因为共享状态的标志变为 ready 而返回。
future_status::timeout1超时,即 `wait_for`(`或 wait_until`) 因为在指定的时间段(或时刻)内共享状态的标志依然没有变为 `ready` 返回。
future_status::deferred2共享状态包含了 deferred 函数。
69 | 70 | 71 | #### 6.5.2.3 `std::launch` 类型 #### 72 | 73 | 该枚举类型主要是在调用 `std::async` 设置异步任务的启动策略的。 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 |
类型描述
launch::asyncAsynchronous: 异步任务会在另外一个线程中调用,并通过共享状态返回异步任务的结果(一般是调用 std::future::get() 获取异步任务的结果)。
launch::deferredDeferred: 异步任务将会在共享状态被访问时调用,相当与按需调用(即延迟(deferred)调用)。
86 | 87 | 请看下例(参考): 88 | 89 | #include // std::cout 90 | #include // std::async, std::future, std::launch 91 | #include // std::chrono::milliseconds 92 | #include // std::this_thread::sleep_for 93 | 94 | void do_print_ten(char c, int ms) 95 | { 96 | for (int i = 0; i < 10; ++i) { 97 | std::this_thread::sleep_for(std::chrono::milliseconds(ms)); 98 | std::cout << c; 99 | } 100 | } 101 | 102 | int main() 103 | { 104 | std::cout << "with launch::async:\n"; 105 | std::future < void >foo = 106 | std::async(std::launch::async, do_print_ten, '*', 100); 107 | std::future < void >bar = 108 | std::async(std::launch::async, do_print_ten, '@', 200); 109 | // async "get" (wait for foo and bar to be ready): 110 | foo.get(); 111 | bar.get(); 112 | std::cout << "\n\n"; 113 | 114 | std::cout << "with launch::deferred:\n"; 115 | foo = std::async(std::launch::deferred, do_print_ten, '*', 100); 116 | bar = std::async(std::launch::deferred, do_print_ten, '@', 200); 117 | // deferred "get" (perform the actual calls): 118 | foo.get(); 119 | bar.get(); 120 | std::cout << '\n'; 121 | 122 | return 0; 123 | } 124 | 125 | 在我的机器上执行结果: 126 | 127 | with launch::async: 128 | *@**@**@**@**@*@@@@@ 129 | 130 | with launch::deferred: 131 | **********@@@@@@@@@@ 132 | -------------------------------------------------------------------------------- /zh/chapter6-Future/6.6 Auxiliary-function.md: -------------------------------------------------------------------------------- 1 | ## 6.6 异步任务辅助函数介绍 ## 2 | 3 | ### 6.6.1 `std::async()` 函数介绍 ### 4 | 5 | 与 `std::future` 相关的函数主要是 `std::async()`,原型如下: 6 | 7 | 8 | 9 | 15 | 16 | 17 | 23 | 24 |
unspecified policy (1) 10 |
template <class Fn, class... Args>
11 | future<typename result_of<Fn(Args...)>::type>
12 | async(Fn&& fn, Args&&... args);
13 | 
14 |
specific policy (2) 18 |
template <class Fn, class... Args>
19 | future<typename result_of<Fn(Args...)>::type>
20 | async(launch policy, Fn&& fn, Args&&... args);
21 | 
22 |
25 | 26 | 上面两组 `std::async()` 的不同之处是第一类 `std::async` 没有指定异步任务(即执行某一函数)的启动策略(launch policy),而第二类函数指定了启动策略,详见 `std::launch` 枚举类型,指定启动策略的函数的 policy 参数可以是 `launch::async`,`launch::deferred`,以及两者的按位或( | )。 27 | 28 | `std::async()` 的 `fn` 和 `args` 参数用来指定异步任务及其参数。另外,`std::async()` 返回一个 `std::future` 对象,通过该对象可以获取异步任务的值或异常(如果异步任务抛出了异常)。 29 | 30 | 下面介绍一下 `std::async` 的用法。 31 | 32 | #include 33 | #include 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | double ThreadTask(int n) { 41 | std::cout << std::this_thread::get_id() 42 | << " start computing..." << std::endl; 43 | 44 | double ret = 0; 45 | for (int i = 0; i <= n; i++) { 46 | ret += std::sin(i); 47 | } 48 | 49 | std::cout << std::this_thread::get_id() 50 | << " finished computing..." << std::endl; 51 | return ret; 52 | } 53 | 54 | int main(int argc, const char *argv[]) 55 | { 56 | std::future f(std::async(std::launch::async, ThreadTask, 100000000)); 57 | 58 | #if 0 59 | while(f.wait_until(std::chrono::system_clock::now() + std::chrono::seconds(1)) 60 | != std::future_status::ready) { 61 | std::cout << "task is running...\n"; 62 | } 63 | #else 64 | while(f.wait_for(std::chrono::seconds(1)) 65 | != std::future_status::ready) { 66 | std::cout << "task is running...\n"; 67 | } 68 | #endif 69 | 70 | std::cout << f.get() << std::endl; 71 | 72 | return EXIT_SUCCESS; 73 | } -------------------------------------------------------------------------------- /zh/chapter6-Future/6.7 Future-multithreading-application.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ewenwan/Cplusplus-Concurrency-In-Practice/8ae68b763434c6babda553d65449e9158d63e357/zh/chapter6-Future/6.7 Future-multithreading-application.md -------------------------------------------------------------------------------- /zh/chapter6-Future/README.md: -------------------------------------------------------------------------------- 1 | # 本章导读 # 2 | 3 | 本章主要内容如下: 4 | 5 | - 异步任务相关的概念以及 C++11 标准 `` 头文件中的类及相关函数的介绍。 6 | - C++11 标准的异步任务基础设施对多线程程序设计带来的影响。 7 | - 异步任务实例。 8 | 9 | # 第六章 异步任务详解 # 10 | 11 | ## 6.1 [`` 头文件摘要](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter6-Future/6.1%20Future-header-synopsis.md) ## 12 | 13 | ## 6.2 [异步任务提供者(Provider) 介绍](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter6-Future/6.2%20Providers-tutorial.md) ## 14 | 15 | ## 6.3 [异步任务提供者(Provider) 介绍(续)](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter6-Future/6.3%20Providers-tutorial-2.md) ## 16 | 17 | ## 6.4 [异步任务 Future 类型详解](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter6-Future/6.4%20Future-tutorial.md) ## 18 | 19 | ## 6.5 [与异步任务相关的类型介绍](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter6-Future/6.5%20Auxiliary-types.md) ## 20 | 21 | ## 6.6 [异步任务辅助函数 `std::async` 介绍](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter6-Future/6.6%20Auxiliary-function.md) ## 22 | 23 | ## 6.7 异步任务与多线程实例 ## 24 | 25 | ## 6.8 [资料汇](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter6-Future/web-resources.md) ## -------------------------------------------------------------------------------- /zh/chapter6-Future/web-resources.md: -------------------------------------------------------------------------------- 1 | # C++ 异步任务资料汇 # 2 | 3 | - C++11 - the new ISO C++ standard: [http://www.stroustrup.com/C++11FAQ.html](http://www.stroustrup.com/C++11FAQ.html) 4 | -------------------------------------------------------------------------------- /zh/chapter7-Atomic/7.1 Atomic-header-synopsis.md: -------------------------------------------------------------------------------- 1 | 本章主要介绍 C++11 标准中的原子类型,首先我们来看 `` 头文件摘要。 2 | 3 | ## 7.1 `` 头文件摘要 ## 4 | 5 | ### 7.1.1 std::atomic_flag 类摘要 ### 6 | 7 | namespace std { 8 | typedef struct atomic_flag { 9 | bool test_and_set(memory_order = memory_order_seq_cst) volatile; 10 | bool test_and_set(memory_order = memory_order_seq_cst); 11 | 12 | void clear(memory_order = memory_order_seq_cst) volatile; 13 | void clear(memory_order = memory_order_seq_cst); 14 | 15 | atomic_flag() = default; 16 | atomic_flag(const atomic_flag&) = delete; 17 | 18 | atomic_flag& operator=(const atomic_flag&) = delete; 19 | atomic_flag& operator=(const atomic_flag&) volatile = delete; 20 | } atomic_flag; 21 | 22 | bool atomic_flag_test_and_set(volatile atomic_flag*); 23 | bool atomic_flag_test_and_set(atomic_flag*); 24 | 25 | bool atomic_flag_test_and_set_explicit(volatile atomic_flag*, memory_order); 26 | bool atomic_flag_test_and_set_explicit(atomic_flag*, memory_order); 27 | 28 | void atomic_flag_clear(volatile atomic_flag*); 29 | void atomic_flag_clear(atomic_flag*); 30 | 31 | void atomic_flag_clear_explicit(volatile atomic_flag*, memory_order); 32 | void atomic_flag_clear_explicit(atomic_flag*, memory_order); 33 | 34 | #define ATOMIC_FLAG_INIT see below 35 | } 36 | 37 | ### 7.1.2 std::atomic 基本类型摘要 ### 38 | 39 | template struct atomic { 40 | bool is_lock_free() const volatile; 41 | bool is_lock_free() const; 42 | 43 | void store(T, memory_order = memory_order_seq_cst) volatile; 44 | void store(T, memory_order = memory_order_seq_cst); 45 | 46 | T load(memory_order = memory_order_seq_cst) const volatile; 47 | T load(memory_order = memory_order_seq_cst) const; 48 | 49 | operator T() const volatile; 50 | operator T() const; 51 | 52 | T exchange(T, memory_order = memory_order_seq_cst) volatile; 53 | T exchange(T, memory_order = memory_order_seq_cst); 54 | 55 | bool compare_exchange_weak(T&, T, memory_order, memory_order) volatile; 56 | bool compare_exchange_weak(T&, T, memory_order, memory_order); 57 | 58 | bool compare_exchange_strong(T&, T, memory_order, memory_order) volatile; 59 | bool compare_exchange_strong(T&, T, memory_order, memory_order); 60 | 61 | bool compare_exchange_weak(T&, T, memory_order = memory_order_seq_cst) volatile; 62 | bool compare_exchange_weak(T&, T, memory_order = memory_order_seq_cst); 63 | 64 | bool compare_exchange_strong(T&, T, memory_order = memory_order_seq_cst) volatile; 65 | bool compare_exchange_strong(T&, T, memory_order = memory_order_seq_cst); 66 | 67 | atomic() = default; 68 | constexpr atomic(T); 69 | atomic(const atomic&) = delete; 70 | 71 | atomic& operator=(const atomic&) = delete; 72 | atomic& operator=(const atomic&) volatile = delete; 73 | T operator=(T) volatile; 74 | T operator=(T); 75 | }; 76 | 77 | 78 | ### 7.1.3 std::atomic 整型特化摘要 ### 79 | 80 | template <> struct atomic { 81 | bool is_lock_free() const volatile; 82 | bool is_lock_free() const; 83 | 84 | void store(integral, memory_order = memory_order_seq_cst) volatile; 85 | void store(integral, memory_order = memory_order_seq_cst); 86 | 87 | integral load(memory_order = memory_order_seq_cst) const volatile; 88 | integral load(memory_order = memory_order_seq_cst) const; 89 | 90 | operator integral() const volatile; 91 | operator integral() const; 92 | 93 | integral exchange(integral, memory_order = memory_order_seq_cst) volatile; 94 | integral exchange(integral, memory_order = memory_order_seq_cst); 95 | 96 | bool compare_exchange_weak(integral&, integral, memory_order, memory_order) volatile; 97 | bool compare_exchange_weak(integral&, integral, memory_order, memory_order); 98 | 99 | bool compare_exchange_strong(integral&, integral, memory_order, memory_order) volatile; 100 | bool compare_exchange_strong(integral&, integral, memory_order, memory_order); 101 | 102 | bool compare_exchange_weak(integral&, integral, memory_order = memory_order_seq_cst) volatile; 103 | bool compare_exchange_weak(integral&, integral, memory_order = memory_order_seq_cst); 104 | 105 | bool compare_exchange_strong(integral&, integral, memory_order = memory_order_seq_cst) volatile; 106 | bool compare_exchange_strong(integral&, integral, memory_order = memory_order_seq_cst); 107 | 108 | integral fetch_add(integral, memory_order = memory_order_seq_cst) volatile; 109 | integral fetch_add(integral, memory_order = memory_order_seq_cst); 110 | integral fetch_sub(integral, memory_order = memory_order_seq_cst) volatile; 111 | integral fetch_sub(integral, memory_order = memory_order_seq_cst); 112 | integral fetch_and(integral, memory_order = memory_order_seq_cst) volatile; 113 | integral fetch_and(integral, memory_order = memory_order_seq_cst); 114 | integral fetch_or(integral, memory_order = memory_order_seq_cst) volatile; 115 | integral fetch_or(integral, memory_order = memory_order_seq_cst); 116 | integral fetch_xor(integral, memory_order = memory_order_seq_cst) volatile; 117 | integral fetch_xor(integral, memory_order = memory_order_seq_cst); 118 | 119 | atomic() = default; 120 | constexpr atomic(integral); 121 | atomic(const atomic&) = delete; 122 | 123 | atomic& operator=(const atomic&) = delete; 124 | atomic& operator=(const atomic&) volatile = delete; 125 | integral operator=(integral) volatile; 126 | integral operator=(integral); 127 | integral operator++(int) volatile; 128 | integral operator++(int); 129 | integral operator--(int) volatile; 130 | integral operator--(int); 131 | integral operator++() volatile; 132 | integral operator++(); 133 | integral operator--() volatile; 134 | integral operator--(); 135 | integral operator+=(integral) volatile; 136 | integral operator+=(integral); 137 | integral operator-=(integral) volatile; 138 | integral operator-=(integral); 139 | integral operator&=(integral) volatile; 140 | integral operator&=(integral); 141 | integral operator|=(integral) volatile; 142 | integral operator|=(integral); 143 | integral operator^=(integral) volatile; 144 | integral operator^=(integral); 145 | }; 146 | 147 | ### 7.1.4 std::atomic 指针特化摘要 ### 148 | 149 | template struct atomic { 150 | bool is_lock_free() const volatile; 151 | bool is_lock_free() const; 152 | 153 | void store(T*, memory_order = memory_order_seq_cst) volatile; 154 | void store(T*, memory_order = memory_order_seq_cst); 155 | 156 | T* load(memory_order = memory_order_seq_cst) const volatile; 157 | T* load(memory_order = memory_order_seq_cst) const; 158 | 159 | operator T*() const volatile; 160 | operator T*() const; 161 | 162 | T* exchange(T*, memory_order = memory_order_seq_cst) volatile; 163 | T* exchange(T*, memory_order = memory_order_seq_cst); 164 | 165 | bool compare_exchange_weak(T*&, T*, memory_order, memory_order) volatile; 166 | bool compare_exchange_weak(T*&, T*, memory_order, memory_order); 167 | 168 | bool compare_exchange_strong(T*&, T*, memory_order, memory_order) volatile; 169 | bool compare_exchange_strong(T*&, T*, memory_order, memory_order); 170 | 171 | bool compare_exchange_weak(T*&, T*, memory_order = memory_order_seq_cst) volatile; 172 | bool compare_exchange_weak(T*&, T*, memory_order = memory_order_seq_cst); 173 | 174 | bool compare_exchange_strong(T*&, T*, memory_order = memory_order_seq_cst) volatile; 175 | bool compare_exchange_strong(T*&, T*, memory_order = memory_order_seq_cst); 176 | 177 | T* fetch_add(ptrdiff_t, memory_order = memory_order_seq_cst) volatile; 178 | T* fetch_add(ptrdiff_t, memory_order = memory_order_seq_cst); 179 | T* fetch_sub(ptrdiff_t, memory_order = memory_order_seq_cst) volatile; 180 | T* fetch_sub(ptrdiff_t, memory_order = memory_order_seq_cst); 181 | 182 | atomic() = default; 183 | constexpr atomic(T*); 184 | atomic(const atomic&) = delete; 185 | atomic& operator=(const atomic&) = delete; 186 | atomic& operator=(const atomic&) volatile = delete; 187 | 188 | T* operator=(T*) volatile; 189 | T* operator=(T*); 190 | T* operator++(int) volatile; 191 | T* operator++(int); 192 | T* operator--(int) volatile; 193 | T* operator--(int); 194 | T* operator++() volatile; 195 | T* operator++(); 196 | T* operator--() volatile; 197 | T* operator--(); 198 | T* operator+=(ptrdiff_t) volatile; 199 | T* operator+=(ptrdiff_t); 200 | T* operator-=(ptrdiff_t) volatile; 201 | T* operator-=(ptrdiff_t); 202 | }; -------------------------------------------------------------------------------- /zh/chapter7-Atomic/7.2 Atomic-flag-tutorial.md: -------------------------------------------------------------------------------- 1 | C++11 并发指南已经写了六章,前六章重点介绍了多线程编程方面的内容,但大部分内容只涉及多线程、互斥量、条件变量和异步编程相关的 API,C++11 程序员完全可以不必知道这些 API 在底层是如何实现的,只需要清楚 C++11 多线程和异步编程相关 API 的语义,然后熟加练习即可应付大部分多线程编码需求。但是在很多极端的场合下为了性能和效率,我们需要开发一些 lock-free 的算法和数据结构,前面几章的内容可能就派不上用场了,因此从本文开始介绍 C++11 标准中 `` 头文件里面的类和相关函数。 2 | 3 | 本文介绍 `` 头文件中最简单的原子类型: `atomic_flag`。`atomic_flag` 一种简单的原子布尔类型,只支持两种操作,`test_and_set` 和 `clear`。 4 | 5 | ## 7.2 `std::atomic_flag` 详解 ## 6 | 7 | ### 7.2.1 `std::atomic_flag` 构造函数 ### 8 | 9 | `std::atomic_flag` 构造函数如下: 10 | 11 | atomic_flag() noexcept = default; 12 | atomic_flag (const atomic_flag&T) = delete; 13 | 14 | `std::atomic_flag` 只有默认构造函数,拷贝构造函数已被禁用,因此不能从其他的 `std::atomic_flag` 对象构造一个新的 `std::atomic_flag` 对象。 15 | 16 | 如果在初始化时没有明确使用 `ATOMIC_FLAG_INIT初始化`,那么新创建的 `std::atomic_flag` 对象的状态是未指定的(unspecified)(既没有被 set 也没有被 clear。)另外,`atomic_flag` 不能被拷贝,也不能 move 赋值。 17 | 18 | `ATOMIC_FLAG_INIT`: 如果某个 `std::atomic_flag `对象使用该宏初始化,那么可以保证该 `std::atomic_flag` 对象在创建时处于 clear 状态。 19 | 20 | 下面先看一个简单的例子,`main()` 函数中创建了 10 个线程进行计数,率先完成计数任务的线程输出自己的 ID,后续完成计数任务的线程不会输出自身 ID: 21 | 22 | #include // std::cout 23 | #include // std::atomic, std::atomic_flag, ATOMIC_FLAG_INIT 24 | #include // std::thread, std::this_thread::yield 25 | #include // std::vector 26 | 27 | std::atomic ready(false); // can be checked without being set 28 | std::atomic_flag winner = ATOMIC_FLAG_INIT; // always set when checked 29 | 30 | void count1m(int id) 31 | { 32 | while (!ready) { 33 | std::this_thread::yield(); 34 | } // 等待主线程中设置 ready 为 true. 35 | 36 | for (int i = 0; i < 1000000; ++i) { 37 | } // 计数. 38 | 39 | // 如果某个线程率先执行完上面的计数过程,则输出自己的 ID. 40 | // 此后其他线程执行 test_and_set 是 if 语句判断为 false, 41 | // 因此不会输出自身 ID. 42 | if (!winner.test_and_set()) { 43 | std::cout << "thread #" << id << " won!\n"; 44 | } 45 | }; 46 | 47 | int main() 48 | { 49 | std::vector threads; 50 | std::cout << "spawning 10 threads that count to 1 million...\n"; 51 | for (int i = 1; i <= 10; ++i) 52 | threads.push_back(std::thread(count1m, i)); 53 | ready = true; 54 | 55 | for (auto & th:threads) 56 | th.join(); 57 | 58 | return 0; 59 | } 60 | 61 | 多次执行结果如下: 62 | 63 | atomic ) ./Atomic-Flag1 64 | spawning 10 threads that count to 1 million... 65 | thread #6 won! 66 | atomic ) ./Atomic-Flag1 67 | spawning 10 threads that count to 1 million... 68 | thread #1 won! 69 | atomic ) ./Atomic-Flag1 70 | spawning 10 threads that count to 1 million... 71 | thread #5 won! 72 | atomic ) ./Atomic-Flag1 73 | spawning 10 threads that count to 1 million... 74 | thread #1 won! 75 | atomic ) ./Atomic-Flag1 76 | spawning 10 threads that count to 1 million... 77 | thread #1 won! 78 | atomic ) ./Atomic-Flag1 79 | spawning 10 threads that count to 1 million... 80 | thread #10 won! 81 | 82 | ### 7.2.2 `test_and_set` 介绍 ### 83 | 84 | `std::atomic_flag` 的 `test_and_set` 函数原型如下: 85 | 86 | bool test_and_set (memory_order sync = memory_order_seq_cst) volatile noexcept; 87 | bool test_and_set (memory_order sync = memory_order_seq_cst) noexcept; 88 | 89 | `test_and_set()` 函数检查 `std::atomic_flag` 标志,如果 `std::atomic_flag` 之前没有被设置过,则设置 `std::atomic_flag` 的标志,并返回先前该 `std::atomic_flag` 对象是否被设置过,如果之前 `std::atomic_flag` 对象已被设置,则返回 `true`,否则返回 `false`。 90 | 91 | test-and-set 操作是原子的(因此 test-and-set 是原子 read-modify-write (RMW)操作)。 92 | 93 | `test_and_set` 可以指定 Memory Order(后续的文章会详细介绍 C++11 的 Memory Order,此处为了完整性列出 `test_and_set` 参数 `sync` 的取值),取值如下: 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 |
Memory Order 值Memory Order 类型
memory_order_relaxedRelaxed
memory_order_consumeConsume
memory_order_acquireAcquire
memory_order_releaseRelease
memory_order_acq_relAcquire/Release
memory_order_seq_cstSequentially consistent
122 | 123 | 一个简单的例子: 124 | 125 | #include // std::cout 126 | #include // std::atomic_flag 127 | #include // std::thread 128 | #include // std::vector 129 | #include // std::stringstream 130 | 131 | std::atomic_flag lock_stream = ATOMIC_FLAG_INIT; 132 | std::stringstream stream; 133 | 134 | void append_number(int x) 135 | { 136 | while (lock_stream.test_and_set()) { 137 | } 138 | stream << "thread #" << x << '\n'; 139 | lock_stream.clear(); 140 | } 141 | 142 | int main() 143 | { 144 | std::vector < std::thread > threads; 145 | for (int i = 1; i <= 10; ++i) 146 | threads.push_back(std::thread(append_number, i)); 147 | for (auto & th:threads) 148 | th.join(); 149 | 150 | std::cout << stream.str() << std::endl;; 151 | return 0; 152 | } 153 | 154 | 执行结果如下: 155 | 156 | thread #1 157 | thread #2 158 | thread #3 159 | thread #4 160 | thread #5 161 | thread #6 162 | thread #7 163 | thread #8 164 | thread #9 165 | thread #10 166 | 167 | ### 7.2.3 `clear()` 介绍 ### 168 | 169 | 清除 `std::atomic_flag` 对象的标志位,即设置 `atomic_flag` 的值为 `false`。`clear` 函数原型如下: 170 | 171 | void clear (memory_order sync = memory_order_seq_cst) volatile noexcept; 172 | void clear (memory_order sync = memory_order_seq_cst) noexcept; 173 | 174 | 清除 `std::atomic_flag` 标志使得下一次调用 `std::atomic_flag::test_and_set` 返回 false。 175 | 176 | `std::atomic_flag::clear()` 可以指定 Memory Order(后续的文章会详细介绍 C++11 的 Memory Order,此处为了完整性列出 `clear` 参数 `sync` 的取值),取值如下: 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 |
Memory Order 值Memory Order 类型
memory_order_relaxedRelaxed
memory_order_consumeConsume
memory_order_acquireAcquire
memory_order_releaseRelease
memory_order_acq_relAcquire/Release
memory_order_seq_cstSequentially consistent
205 | 206 | 结合 `std::atomic_flag::test_and_set()` 和 `std::atomic_flag::clear()`,`std::atomic_flag` 对象可以当作一个简单的自旋锁使用,请看下例: 207 | 208 | #include 209 | #include 210 | #include 211 | #include 212 | 213 | std::atomic_flag lock = ATOMIC_FLAG_INIT; 214 | 215 | void f(int n) 216 | { 217 | for (int cnt = 0; cnt < 100; ++cnt) { 218 | while (lock.test_and_set(std::memory_order_acquire)) // acquire lock 219 | ; // spin 220 | std::cout << "Output from thread " << n << '\n'; 221 | lock.clear(std::memory_order_release); // release lock 222 | } 223 | } 224 | 225 | int main() 226 | { 227 | std::vector v; 228 | for (int n = 0; n < 10; ++n) { 229 | v.emplace_back(f, n); 230 | } 231 | for (auto& t : v) { 232 | t.join(); 233 | } 234 | } 235 | 236 | 在上面的程序中,`std::atomic_flag` 对象 `lock` 的上锁操作可以理解为 `lock.test_and_set(std::memory_order_acquire);` (此处指定了 Memory Order,更多有关 Memory Order 的概念,我会在后续的文章中介绍),解锁操作相当与 `lock.clear(std::memory_order_release)`。 237 | 238 | 在上锁的时候,如果 `lock.test_and_set` 返回 `false`,则表示上锁成功(此时 `while` 不会进入自旋状态),因为此前 `lock` 的标志位为 `false`(即没有线程对 `lock` 进行上锁操作),但调用 `test_and_set` 后 `lock` 的标志位为 `true`,说明某一线程已经成功获得了 `lock` 锁。 239 | 240 | 解锁操作非常简单,直接调用 `lock.clear()` 即可,此后其他线程可以继续对该 `std::atomic_flag` 对象(`lock`)进行上锁和解锁操作。 241 | 242 | 如果在该线程解锁(即调用 `lock.clear(std::memory_order_release)`) 之前,另外一个线程也调用 `lock.test_and_set(std::memory_order_acquire)` 试图获得锁,则 `test_and_set(std::memory_order_acquire)` 返回 `true`,则 `while` 进入自旋状态。如果获得锁的线程解锁(即调用了 `lock.clear(std::memory_order_release)`)之后,另外某个线程试图调用 `lock.test_and_set(std::memory_order_acquire)` 并且返回 `false`,则 `while` 不会进入自旋,此时该线程也成功地获得了锁。 243 | 244 | 按照上面的分析,我们知道在某种情况下 `std::atomic_flag` 对象可以当作一个简单的自旋锁使用。 -------------------------------------------------------------------------------- /zh/chapter7-Atomic/7.4 Atomic-tutorial2.md: -------------------------------------------------------------------------------- 1 | 上一节介绍了基本的原子类型 `std::atomic` 的用法,本节我会给大家介绍 C++11 标准库中的 `std::atomic` 针对整形(`integral`)和指针类型的特化版本做了哪些改进。 2 | 3 | ## 7.4 特化的 `std::atomic` 类型详解 ## 4 | 5 | 总地来说,C++11 标准库中的 `std::atomic` 针对整形(`integral`)和指针类型的特化版本新增了一些算术运算和逻辑运算操作。具体如下: 6 | 7 | integral fetch_add(integral, memory_order = memory_order_seq_cst) volatile; 8 | integral fetch_add(integral, memory_order = memory_order_seq_cst); 9 | integral fetch_sub(integral, memory_order = memory_order_seq_cst) volatile; 10 | integral fetch_sub(integral, memory_order = memory_order_seq_cst); 11 | integral fetch_and(integral, memory_order = memory_order_seq_cst) volatile; 12 | integral fetch_and(integral, memory_order = memory_order_seq_cst); 13 | integral fetch_or(integral, memory_order = memory_order_seq_cst) volatile; 14 | integral fetch_or(integral, memory_order = memory_order_seq_cst); 15 | integral fetch_xor(integral, memory_order = memory_order_seq_cst) volatile; 16 | integral fetch_xor(integral, memory_order = memory_order_seq_cst); 17 | 18 | integral operator++(int) volatile; 19 | integral operator++(int); 20 | integral operator--(int) volatile; 21 | integral operator--(int); 22 | integral operator++() volatile; 23 | integral operator++(); 24 | integral operator--() volatile; 25 | integral operator--(); 26 | integral operator+=(integral) volatile; 27 | integral operator+=(integral); 28 | integral operator-=(integral) volatile; 29 | integral operator-=(integral); 30 | integral operator&=(integral) volatile; 31 | integral operator&=(integral); 32 | integral operator|=(integral) volatile; 33 | integral operator|=(integral); 34 | integral operator^=(integral) volatile; 35 | integral operator^=(integral); 36 | 37 | 下面我们来简单介绍以上的 `std::atomic` 特化版本的成员函数。 38 | 39 | 40 | ### 7.4.1 `fetch_add` ### 41 | 42 | 43 | 44 | 49 | 50 | 51 | 56 | 57 |
if T is integral (1) 45 |
T fetch_add (T val, memory_order sync = memory_order_seq_cst) volatile noexcept;
 46 | T fetch_add (T val, memory_order sync = memory_order_seq_cst) noexcept;
 47 | 
48 |
if T is pointer (2) 52 |
T fetch_add (ptrdiff_t val, memory_order sync = memory_order_seq_cst) volatile noexcept;
 53 | T fetch_add (ptrdiff_t val, memory_order sync = memory_order_seq_cst) noexcept;
 54 | 
55 |
58 | 59 | 将原子对象的封装值加 `val`,并返回原子对象的旧值(适用于整形和指针类型的 `std::atomic` 特化版本),整个过程是原子的。`sync` 参数指定内存序: 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 |
Memory Order 值Memory Order 类型
memory_order_relaxedRelaxed
memory_order_consumeConsume
memory_order_acquireAcquire
memory_order_releaseRelease
memory_order_acq_relAcquire/Release
memory_order_seq_cstSequentially consistent
88 | 89 | 另外,如果第二个参数不指定(取默认参数 `memory_order_seq_cst`),则 `fetch_add` 相当与 `std::atomic::operator+=`。 90 | 91 | ### 7.4.2 `fetch_sub` ### 92 | 93 | 94 | 95 | 100 | 101 | 102 | 107 | 108 |
if T is integral (1) 96 |
T fetch_add (T val, memory_order sync = memory_order_seq_cst) volatile noexcept;
 97 | T fetch_add (T val, memory_order sync = memory_order_seq_cst) noexcept;
 98 | 
99 |
if T is pointer (2) 103 |
T fetch_add (ptrdiff_t val, memory_order sync = memory_order_seq_cst) volatile noexcept;
104 | T fetch_add (ptrdiff_t val, memory_order sync = memory_order_seq_cst) noexcept;
105 | 
106 |
109 | 110 | 将原子对象的封装值减 `val`,并返回原子对象的旧值(适用于整形和指针类型的 `std::atomic` 特化版本),整个过程是原子的。`sync` 参数指定内存序: 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 |
Memory Order 值Memory Order 类型
memory_order_relaxedRelaxed
memory_order_consumeConsume
memory_order_acquireAcquire
memory_order_releaseRelease
memory_order_acq_relAcquire/Release
memory_order_seq_cstSequentially consistent
139 | 140 | 另外,如果第二个参数不指定(取默认参数 `memory_order_seq_cst`),则 `fetch_sub` 相当与 `std::atomic::operator-=`。 141 | 142 | ### 7.4.3 `fetch_and` ### 143 | 144 | T fetch_and (T val, memory_order sync = memory_order_seq_cst) volatile noexcept; 145 | T fetch_and (T val, memory_order sync = memory_order_seq_cst) noexcept; 146 | 147 | 将原子对象的封装值按位与 `val`,并返回原子对象的旧值(只适用于整型的 `std::atomic` 特化版本),整个过程是原子的。`sync` 参数指定内存序: 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 |
Memory Order 值Memory Order 类型
memory_order_relaxedRelaxed
memory_order_consumeConsume
memory_order_acquireAcquire
memory_order_releaseRelease
memory_order_acq_relAcquire/Release
memory_order_seq_cstSequentially consistent
176 | 177 | 另外,如果第二个参数不指定(取默认参数 `memory_order_seq_cst`),则 `fetch_and` 相当与 `std::atomic::operator&=`。 178 | 179 | ### 7.4.4 `fetch_or` ### 180 | 181 | T fetch_or (T val, memory_order sync = memory_order_seq_cst) volatile noexcept; 182 | T fetch_or (T val, memory_order sync = memory_order_seq_cst) noexcept; 183 | 184 | 将原子对象的封装值按位或 `val`,并返回原子对象的旧值(只适用于整型的 `std::atomic` 特化版本),整个过程是原子的。`sync` 参数指定内存序: 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 |
Memory Order 值Memory Order 类型
memory_order_relaxedRelaxed
memory_order_consumeConsume
memory_order_acquireAcquire
memory_order_releaseRelease
memory_order_acq_relAcquire/Release
memory_order_seq_cstSequentially consistent
213 | 214 | 另外,如果第二个参数不指定(取默认参数 `memory_order_seq_cst`),则 `fetch_or` 相当与 `std::atomic::operator|=`。 215 | 216 | ### 7.4.5 `fetch_xor` ### 217 | 218 | T fetch_xor (T val, memory_order sync = memory_order_seq_cst) volatile noexcept; 219 | T fetch_xor (T val, memory_order sync = memory_order_seq_cst) noexcept; 220 | 221 | 将原子对象的封装值按位异或 `val`,并返回原子对象的旧值(只适用于整型的 `std::atomic` 特化版本),整个过程是原子的。`sync` 参数指定内存序: 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 |
Memory Order 值Memory Order 类型
memory_order_relaxedRelaxed
memory_order_consumeConsume
memory_order_acquireAcquire
memory_order_releaseRelease
memory_order_acq_relAcquire/Release
memory_order_seq_cstSequentially consistent
250 | > 251 | 另外,如果第二个参数不指定(取默认参数 `memory_order_seq_cst`),则 `fetch_xor` 相当与 `std::atomic::operator^=`。 252 | 253 | ### 7.4.6 `operator++` ### 254 | 255 | 256 | 257 | 262 | 263 | 264 | 269 | 270 |
pre-increment (1) 258 |
T operator++() volatile noexcept;
259 | T operator++() noexcept;
260 | 
261 |
post-increment (2) 265 |
T operator++ (int) volatile noexcept;
266 | T operator++ (int) noexcept;
267 | 
268 |
271 | 272 | 自增运算符重载, 第一种形式 (1) 返回自增后的值(即前缀++),第二种形式(2) 返回自增前的值(即后缀++),适用于整形和指针类型的 `std::atomic` 特化版本。 273 | 274 | ### 7.4.7 `operator--` ### 275 | 276 | 277 | 278 | 283 | 284 | 285 | 290 | 291 |
pre-decrement (1) 279 |
T operator--() volatile noexcept;
280 | T operator--() noexcept;
281 | 
282 |
post-decrement (2) 286 |
T operator-- (int) volatile noexcept;
287 | T operator-- (int) noexcept;
288 | 
289 |
292 | 293 | 自减运算符重载, 第一种形式 (1) 返回自减后的值(即前缀--),第二种形式(2) 返回自减前的值(即后缀--),适用于整形和指针类型的 `std::atomic` 特化版本。 294 | 295 | ### 7.4.8 `atomic::operator (comp. assign.)` ### 296 | 297 | 复合赋值运算符重载,主要包含以下形式: 298 | 299 | 300 | 301 | 314 | 315 | 316 | 323 | 324 |
if T is integral (1) 302 |
T operator+= (T val) volatile noexcept;
303 | T operator+= (T val) noexcept;
304 | T operator-= (T val) volatile noexcept;
305 | T operator-= (T val) noexcept;
306 | T operator&= (T val) volatile noexcept;
307 | T operator&= (T val) noexcept;
308 | T operator|= (T val) volatile noexcept;
309 | T operator|= (T val) noexcept;
310 | T operator^= (T val) volatile noexcept;
311 | T operator^= (T val) noexcept;
312 | 
313 |
if T is pointer (2) 317 |
T operator+= (ptrdiff_t val) volatile noexcept;
318 | T operator+= (ptrdiff_t val) noexcept;
319 | T operator-= (ptrdiff_t val) volatile noexcept;
320 | T operator-= (ptrdiff_t val) noexcept;
321 | 
322 |
325 | 326 | 以上各个 operator 都会有对应的 `fetch_*` 操作,详细见下表: 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 |
操作符成员函数支持类型
复合赋值等价于整型指针类型其他类型
+atomic::operator+=atomic::fetch_add
-atomic::operator-=atomic::fetch_sub
&atomic::operator&=atomic::fetch_and
|atomic::operator|=atomic::fetch_or
^atomic::operator^=atomic::fetch_xor
372 | 373 | 好了,本节先介绍这里,下一节我会介绍 C++11 中 C 风格的原子操作 API。 -------------------------------------------------------------------------------- /zh/chapter7-Atomic/README.md: -------------------------------------------------------------------------------- 1 | # 本章导读 # 2 | 3 | 本章主要内容如下: 4 | 5 | - 什么是原子类型,原子类型的适用场景。 6 | - C++11 标准中 `` 头文件中的类及相关函数的介绍。 7 | - 利用原子变量 `std::atomic` 进行线程同步的应用实例。 8 | 9 | # 第七章 原子类型详解 # 10 | 11 | ## 7.1 [`` 头文件摘要](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter7-Atomic/7.1%20Atomic-header-synopsis.md) ## 12 | 13 | ## 7.2 [`std::atomic_flag` 详解](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter7-Atomic/7.2%20Atomic-flag-tutorial.md) ## 14 | 15 | ## 7.3 [基本 `std::atomic` 类型详解](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter7-Atomic/7.3%20Atomic-tutorial.md) ## 16 | 17 | ## 7.4 [特化的 `std::atomic` 类型详解](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter7-Atomic/7.4%20Atomic-tutorial2.md) ## 18 | 19 | ## 7.5 [C 风格的原子操作](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter7-Atomic/7.5%20C-style-atomic.md) ## 20 | 21 | ## 7.6 C++11 内存模型初探 ## 22 | 23 | ## 7.7 如何利用原子类型设计并发数据结构 ## 24 | 25 | ## 7.8 [资料汇](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter7-Atomic/web-resources.md) ## -------------------------------------------------------------------------------- /zh/chapter7-Atomic/web-resources.md: -------------------------------------------------------------------------------- 1 | # C++ 原子类型详解 # 2 | 3 | - C++11 - the new ISO C++ standard: [http://www.stroustrup.com/C++11FAQ.html](http://www.stroustrup.com/C++11FAQ.html) 4 | - Common Pitfalls in Writing Lock-Free Algorithms: [http://blog.memsql.com/common-pitfalls-in-writing-lock-free-algorithms/](http://blog.memsql.com/common-pitfalls-in-writing-lock-free-algorithms/) 5 | - https://www.kernel.org/doc/Documentation/memory-barriers.txt: [https://www.kernel.org/doc/Documentation/memory-barriers.txt](https://www.kernel.org/doc/Documentation/memory-barriers.txt) 6 | - Choose Concurrency-Friendly Data Structures: [http://www.drdobbs.com/parallel/choose-concurrency-friendly-data-structu/208801371](http://www.drdobbs.com/parallel/choose-concurrency-friendly-data-structu/208801371) 7 | - Lock-Free Data Structures: [http://www.drdobbs.com/lock-free-data-structures/184401865](http://www.drdobbs.com/lock-free-data-structures/184401865) 8 | - Concurrent Programming with Chain Locking: [http://www.drdobbs.com/parallel/concurrent-programming-with-chain-lockin/240149442?pgno=1](http://www.drdobbs.com/parallel/concurrent-programming-with-chain-lockin/240149442?pgno=1) 9 | - Practical lock-free data structures: [http://www.cl.cam.ac.uk/research/srg/netos/lock-free/](http://www.cl.cam.ac.uk/research/srg/netos/lock-free/) 10 | - 透过 Linux 内核看无锁编程: [http://www.ibm.com/developerworks/cn/linux/l-cn-lockfree/index.html](http://www.ibm.com/developerworks/cn/linux/l-cn-lockfree/index.html) 11 | - Non-blocking algorithm: [http://en.wikipedia.org/wiki/Non-blocking_synchronization](http://en.wikipedia.org/wiki/Non-blocking_synchronization) -------------------------------------------------------------------------------- /zh/chapter8-Memory-Model/8.1 C++-program-layout.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ewenwan/Cplusplus-Concurrency-In-Practice/8ae68b763434c6babda553d65449e9158d63e357/zh/chapter8-Memory-Model/8.1 C++-program-layout.md -------------------------------------------------------------------------------- /zh/chapter8-Memory-Model/8.2 C++11 memory-model-explained.md: -------------------------------------------------------------------------------- 1 | 前一章主要介绍了 C++11 中的原子类型及其相关的 API,原子类型的大多数 API 都需要程序员提供一个 `std::memory_order`(可译为内存序,访存顺序) 的枚举类型值作为参数,比如:`atomic_store`,`atomic_load`,`atomic_exchange`,`atomic_compare_exchange` 等 API 的最后一个形参为 `std::memory_order order`,默认值是 `std::memory_order_seq_cst`(顺序一致性)。那么究竟什么是 `std::memory_order` 呢,为了解答这个问题,我们先来讨论 C++11 的内存模型。 2 | 3 | 一般来讲,内存模型可分为静态内存模型和动态内存模型,静态内存模型主要涉及类的对象在内存中是如何存放的,即从结构(structural)方面来看一个对象在内存中的布局,以一个简单的例子为例(截图参考《C++ Concurrency In Action》 P105 ): 4 | 5 | ![](https://raw.github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/master/images/chapter9/object-layout.png) 6 | 7 | 上面是一个简单的 C++ 类(又称POD: Plain Old Data,它没有虚函数,没有继承),它在内存中的布局如图右边所示(对于复杂类对象的内存布局,请参考《深度探索C++对象模型》一书)。 8 | 9 | 动态内存模型可理解为存储一致性模型,主要是从行为(behavioral)方面来看多个线程对同一个对象同时(读写)操作时(concurrency)所做的约束,动态内存模型理解起来稍微复杂一些,涉及了内存,Cache,CPU 各个层次的交互,尤其是在共享存储系统中,为了保证程序执行的正确性,就需要对访存事件施加严格的限制。 10 | 11 | 文献中常见的存储一致性模型包括顺序一致性模型,处理器一致性模型,弱一致性模型,释放一致性模型,急切更新释放一致性模型、懒惰更新释放一致性模型,域一致性模型以及单项一致性模型。不同的存储一致性模型对访存事件次序的限制不同,因而对程序员的要求和所得到的的性能也不一样。存储一致性模型对访存事件次序施加的限制越弱,我们就越有利于提高程序的性能,但编程实现上更困难。 12 | 13 | 顺序一致性模型由 Lamport 于 1979 年提出。顺序一致性模型最好理解但代价太大,原文指出: 14 | 15 | > ... the result of any execution is the same as if the operations of all the processors were executed in some sequential order, and the operations of each individual processor appear in this sequence in the order specified by its program. 16 | 17 | 该模型指出:如果在共享存储系统中多机并行执行的结果等于把每一个处理器所执行的指令流按照某种方式顺序地交织在一起在单机上执行的结果,则该共享存储系统是顺序一致性的。 18 | 19 | 顺序一致性不仅在共享存储系统上适用,在多处理器和多线程环境下也同样适用。而在多处理器和多线程环境下理解顺序一致性包括两个方面,(1). 从多个线程平行角度来看,程序最终的执行结果相当于多个线程某种交织执行的结果,(2)从单个线程内部执行顺序来看,该线程中的指令是按照程序事先已规定的顺序执行的(即不考虑运行时 CPU 乱序执行和 Memory Reorder)。 20 | 21 | 我们以一个具体的例子来理解顺序一致性: 22 | 23 | 假设存在两个共享变量a, b,初始值均为 0,两个线程运行不同的指令,如下表格所示,线程 1 设置 a 的值为 1,然后设置 R1 的值为 b,线程 2 设置 b 的值为 2,并设置 R2 的值为 a,请问在不加任何锁或者其他同步措施的情况下,R1,R2 的最终结果会是多少? 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 |
线程 1线程 2
a = 1;b = 2;
R1 = b;R2 = a;
41 | 42 | 由于没有施加任何同步限制,两个线程将会交织执行,但交织执行时指令不发生重排,即线程 1 中的 a = 1 始终在 R1 = b 之前执行,而线程 2 中的 b = 2 始终在 R2 = a 之前执行 ,因此可能的执行序列共有 `4!/(2!*2!)` = 6 种: 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 58 | 61 | 64 | 67 | 70 | 73 | 74 | 75 | 78 | 81 | 84 | 87 | 90 | 93 | 94 | 95 | 98 | 101 | 104 | 107 | 110 | 113 | 114 | 115 | 118 | 121 | 124 | 127 | 130 | 133 | 134 | 135 | 138 | 141 | 144 | 147 | 150 | 153 | 154 | 155 |
情况 1情况 2情况 3情况 4情况 5情况 6
56 |
a = 1;
57 |
59 |
b = 2;
60 |
62 |
a = 1;
63 |
65 |
a = 1;
66 |
68 |
b = 2;
69 |
71 |
b = 2;
72 |
76 |
R1 = b;
77 |
79 |
R2 = a;
80 |
82 |
b = 2;
83 |
85 |
b = 2;
86 |
88 |
a = 1;
89 |
91 |
a = 1;
92 |
96 |
b = 2;
97 |
99 |
a = 1;
100 |
102 |
R1 = b;
103 |
105 |
R2 = a;
106 |
108 |
R1 = b;
109 |
111 |
R2 = b;
112 |
116 |
R2 = a;
117 |
119 |
R1 = b;
120 |
122 |
R2 = a;
123 |
125 |
R1 = b;
126 |
128 |
R2 = a;
129 |
131 |
R1 = b;
132 |
136 |
R1 == 0, R2 == 1
137 |
139 |
R1 == 2, R2 == 0
140 |
142 |
R1 == 2, R2 == 1
143 |
145 |
R1 == 2, R2 == 1
146 |
148 |
R1 == 2, R2 == 1
149 |
151 |
R1 == 2, R2 == 1
152 |
156 | 157 | 上面的表格列举了两个线程交织执行时所有可能的执行序列,我们发现,R1,R2 最终结果只有 3 种情况,分别是 R1 == 0, R2 == 1(情况 1),R1 == 2, R2 == 0(情况2) 和 R1 == 2, R2 == 1(情况 3, 4, 5,6)。结合上面的例子,我想大家应该理解了什么是顺序一致性。 158 | 159 | 因此,多线程环境下顺序一致性包括两个方面,(1). 从多个线程平行角度来看,程序最终的执行结果相当于多个线程某种交织执行的结果,(2)从单个线程内部执行顺序来看,该线程中的指令是按照程序事先已规定的顺序执行的(即不考虑运行时 CPU 乱序执行和 Memory Reorder)。 160 | 161 | 当然,顺序一致性代价太大,不利于程序的优化,现在的编译器在编译程序时通常将指令重新排序(当然前提是保证程序的执行结果是正确的),例如,如果两个变量读写互不相关,编译器有可能将读操作提前(暂且称为预读prefetch 吧),或者尽可能延迟写操作,假设如下面的代码段: 162 | 163 | int a = 1, b = 2; 164 | 165 | void func() 166 | { 167 | a = b + 22; 168 | b = 22; 169 | } 170 | 171 | 在GCC 4.4 (X86-64)编译条件下,优化选项为 -O0 时,汇编后关键代码如下: 172 | 173 | movl b(%rip), %eax ; 将 b 读入 %eax 174 | addl $22, %eax ; %eax 加 22, 即 b + 22 175 | movl %eax, a(%rip) ; % 将 %eax 写回至 a, 即 a = b + 22 176 | movl $22, b(%rip) ; 设置 b = 22 177 | 178 | 而在设置 -O2 选项时,汇编后的关键代码如下: 179 | 180 | movl b(%rip), %eax ; 将 b 读入 %eax 181 | movl $22, b(%rip) ; b = 22 182 | addl $22, %eax ; %eax 加 22 183 | movl %eax, a(%rip) ; 将 b + 22 的值写入 a,即 a = b + 2 184 | 185 | 由上面的例子可以看出,编译器在不同的优化级别下确实对指令进行了不同程度重排,在 -O0(不作优化)的情况下,汇编指令和 C 源代码的逻辑相同,但是在 -O2 优化级别下,汇编指令和原始代码的执行逻辑不同,由汇编代码可以观察出,b = 22 首先执行,最后才是 a = b + 2, 由此看出,编译器会根据不同的优化等级来适当地对指令进行重排。在单线程条件下上述指令重排不会对执行结果带来任何影响,但是在多线程环境下就不一定了。如果另外一个线程依赖 a,b的值来选择它的执行逻辑,那么上述重排将会产生严重问题。编译器优化是一门深奥的技术,但是无论编译器怎么优化,都需要对优化条件作出约束,尤其是在多线程条件下,不能无理由地优化,更不能错误地优化。 186 | 187 | 另外,现代的 CPU 大都支持多发射和乱序执行,在乱序执行时,指令被执行的逻辑可能和程序汇编指令的逻辑不一致,在单线程条件下,CPU 的乱序执行不会带来大问题,但是在多核多线程时代,当多线程共享某一变量时,不同线程对共享变量的读写就应该格外小心,不适当的乱序执行可能导致程序运行错误。因此,CPU 的乱序执行也需要作出适当的约束。 188 | 189 | 综上所述,我们必须对编译器和 CPU 作出一定的约束才能合理正确地优化你的程序,那么这个约束是什么呢?答曰:内存模型。C++程序员要想写出高性能的多线程程序必须理解内存模型,编译器会给你的程序做优化(静态),CPU为了提升性能也有乱序执行(动态),总之,程序在最终执行时并不会按照你之前的原始代码顺序来执行,因此内存模型是程序员、编译器,CPU 之间的契约,遵守契约后大家就各自做优化,从而尽可能提高程序的性能。 190 | 191 | C++11 中规定了 6 中访存次序(Memory Order),如下: 192 | 193 | enum memory_order { 194 | memory_order_relaxed, 195 | memory_order_consume, 196 | memory_order_acquire, 197 | memory_order_release, 198 | memory_order_acq_rel, 199 | memory_order_seq_cst 200 | }; 201 | 202 | 203 | `std::memory_order` 规定了普通访存操作和相邻的原子访存操作之间的次序是如何安排的,在多核系统中,当多个线程同时读写多个变量时,其中的某个线程所看到的变量值的改变顺序可能和其他线程写入变量值的次序不相同。同时,不同的线程所观察到的某变量被修改次序也可能不相同。然而,如果保证所有对原子变量的操作都是顺序的话,可能对程序的性能影响很大,因此,我们可以通过 `std::memory_order` 来指定编译器对访存次序所做的限制。因此,在原子类型的 API 中,我们可以通过额外的参数指定该原子操作的访存次序(内存序),默认的内存序是 `std::memory_order_seq_cst`。 204 | 205 | 我们可以把上述 6 中访存次序(内存序)分为 3 类,顺序一致性模型(`std::memory_order_seq_cst`),Acquire-Release 模型(`std::memory_order_consume`, `std::memory_order_acquire`, `std::memory_order_release`, `std::memory_order_acq_rel`,) 和 Relax 模型(`std::memory_order_relaxed`)。三种不同的内存模型在不同类型的 CPU上(如 X86,ARM,PowerPC等)所带来的代价也不一样。例如,在 X86 或者 X86-64平台下,Acquire-Release 类型的访存序不需要额外的指令来保证原子性,即使顺序一致性类型操作也只需要在写操作(Store)时施加少量的限制,而在读操作(Load)则不需要花费额外的代价来保证原子性。 206 | 207 | 本文剩余部分将介绍其他的存储器一致模型中的其他几种较常见的模型:处理器一致性(Processor Consistency)模型,弱一致性(Weak Consistency)模型,释放一致性(Release Consistency)模型。 208 | 209 | > 注:以下内容来自中国科学院计算技术研究所胡伟武老师写的《计算机体系结构》(清华大学出版社),该书是胡伟武老师给研究生讲课所用的教材,本文略有删改 210 | 211 | 处理器一致性(Processor Consistency)模型:处理器一致性(Processor Consistency)模型比顺序一致性模型弱,因此对于某些在顺序一致性模型下能够正确执行的程序在处理器一致性条件下执行时可能会导致错误的结果,处理器一致性模型对访存事件发生次序施加的限制是:(1). 在任意读操作(Load)被允许执行之前,所有在同一处理器中先于这一 Load 的读操作都已完成;(2). 在任意写操作(Store)被允许执行之前,所有在同一处理器中先于这一 Store 的访存操作(包括 Load 和 Store操作)都已完成。上述条件允许 Store 之后的 Load 越过 Store 操作而有限执行。 212 | 213 | 弱一致性(Weak Consistency)模型:弱一致性(Weak Consistency)模型的主要思想是将同步操作和普通的访存操作区分开来,程序员必须用硬件可识别的同步操作把对可写共享单元的访存保护起来,以保证多个处理器对可写单元的访问是互斥的。弱一致性对访存事件发生次序的限制如下:(1). 同步操作的执行满足顺序一致性条件; (2). 在任一普通访存操作被允许执行之前,所有在同一处理器中先于这一访存操作的同步操作都已完成; (3). 在任一同步操作被允许执行之前,所有在同一处理器中先于这一同步操作的普通操作都已完成。上述条件允许在同步操作之间的普通访存操作执行时不用考虑进程之间的相关,虽然弱一致性增加了程序员的负担,但是它能有效地提高系统的性能。 214 | 215 | 释放一致性(Release Consistency)模型:释放一致性(Release Consistency)模型是对弱一致性(Weak Consistency)模型的改进,它把同步操作进一步分成了获取操作(Acquire)和释放操作(Release)。Acquire 用于获取对某些共享变量的独占访问权,而 Release 则用于释放这种访问权,释放一致性(Release Consistency)模型访存事件发生次序的限制如下:(1). 同步操作的执行满足顺序一致性条件; (2). 在任一普通访存操作被允许执行之前,所有在同一处理器中先于这一访存操作的 Acquire 操作都已完成; (3). 在任一 Release 操作被允许执行之前,所有在同一处理器中先于这一 Release 操作的普通操作都已完成。 216 | 217 | 在硬件实现的释放一致性模型中,对共享单元的访存是及时进行的,并在执行获取操作(Acquire)和释放操作(Release)时对齐。在共享虚拟存储系统或者在由软件维护的数据一致性的共享存储系统中,由于通信和数据交换的开销很大,有必要减少通信和数据交换的次数。为此,人们在释放一致性(Release Consistency)模型的基础上提出了急切更新释放一致性模型(Eager Release Consistency)和懒惰更新释放一致性模型(Lazy Release Consistency)。在急切更新释放一致性模型中,在临界区内的多个存数操作对共享内存的更新不是及时进行的,而是在执行 Release 操作之前(即退出临界区之前)集中进行,把多个存数操作合并在一起统一执行,从而减少了通信次数。而在懒惰更新释放一致性模型中,由一个处理器对某单元的存数操作并不是由此处理器主动传播到所有共享该单元的其他处理器,而是在其他处理器要用到此处理器所写的数据时(即其他处理器执行 Acquire 操作时)再向此处理器索取该单元的最新备份,这样可以进一步减少通信量。 218 | 219 | 好了,本文主要介绍了内存模型的相关概念,并重点介绍了顺序一致性模型(附带介绍了几种常见的存储一致性模型),并以一个实际的小例子向大家介绍了为什么程序员需要理解内存模型,总之,C++ 程序员要想写出高性能的多线程程序必须理解内存模型,因为编译器会给你的程序做优化(如指令重排等),CPU 为了提升性能也有多发射和乱序执行,因此程序在最终执行时并不会按照你之前的原始代码顺序来执行,所以内存模型是程序员、编译器,CPU 之间的契约,遵守契约后大家就各自做优化,从而尽可能提高程序的性能。 220 | 221 | 下一节我将给大家介绍 C++11 内存模型中的 6 种访存次序(或内存序)(`std::memory_order_relaxed`, `std::memory_order_consume`, `std::memory_order_acquire`, `std::memory_order_release`, `std::memory_order_acq_rel`, `std::memory_order_seq_cst`)各自的意义以及常见的用法,希望感兴趣的同学继续关注,如果您发现文中的错误,一定尽快告诉我 ;-) 222 | -------------------------------------------------------------------------------- /zh/chapter8-Memory-Model/8.3 Synchronization-and-ordering.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ewenwan/Cplusplus-Concurrency-In-Practice/8ae68b763434c6babda553d65449e9158d63e357/zh/chapter8-Memory-Model/8.3 Synchronization-and-ordering.md -------------------------------------------------------------------------------- /zh/chapter8-Memory-Model/Memory-ordering-in-C++11.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ewenwan/Cplusplus-Concurrency-In-Practice/8ae68b763434c6babda553d65449e9158d63e357/zh/chapter8-Memory-Model/Memory-ordering-in-C++11.md -------------------------------------------------------------------------------- /zh/chapter8-Memory-Model/README.md: -------------------------------------------------------------------------------- 1 | # 本章导读 # 2 | 3 | 本章主要介绍 C++11 的内存模型,并结合前一章介绍的原子类型给出相应的实例。主要内容包括: 4 | 5 | - C++ 内存模型概述 6 | - X86 CPU 处理器架构与存储一致性模型简介 7 | - 内存序(Memory Order)与同步操作 8 | - 原子类型操作中内存序(Memory Order)参数的选取与编程实例 9 | - Lock-free 编程初探 10 | 11 | ## 8.1 C++ 内存模型概述 ## 12 | ## 8.2 X86 CPU 处理器架构与常见的存储一致性模型简介 ## 13 | ## 8.3 内存序(Memory Order)与同步操作 14 | ## 8.4 原子类型编程实例 ## 15 | ## 8.5 Lock-free 编程初探 ## 16 | ## [资料汇](https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial/blob/master/zh/chapter8-Memory-Model/web-resources.md) ## -------------------------------------------------------------------------------- /zh/chapter8-Memory-Model/web-resources.md: -------------------------------------------------------------------------------- 1 | # C++ 多线程与内存模型资料汇 # 2 | 3 | ## [Bartosz Milewski's Programming Cafe](http://bartoszmilewski.com/ "Bartosz Milewski's Programming Cafe") ## 4 | 5 | - The Language of Concurrency Video: [http://bartoszmilewski.com/2011/06/27/the-language-of-concurrency-video/](http://bartoszmilewski.com/2011/06/27/the-language-of-concurrency-video/) 6 | 7 | - C++ atomics and memory ordering: [http://bartoszmilewski.com/2008/12/01/c-atomics-and-memory-ordering/](http://bartoszmilewski.com/2008/12/01/c-atomics-and-memory-ordering/) 8 | 9 | - Who ordered memory fences on an x86?: [http://bartoszmilewski.com/2008/11/05/who-ordered-memory-fences-on-an-x86/](http://bartoszmilewski.com/2008/11/05/who-ordered-memory-fences-on-an-x86/) 10 | 11 | - Who ordered sequential consistency?: [http://bartoszmilewski.com/2008/11/11/who-ordered-sequential-consistency/](http://bartoszmilewski.com/2008/11/11/who-ordered-sequential-consistency/) 12 | 13 | - The Future of C++ Concurrency and Parallelism: [http://bartoszmilewski.com/2012/05/11/the-future-of-c-concurrency-and-parallelism/](http://bartoszmilewski.com/2012/05/11/the-future-of-c-concurrency-and-parallelism/) 14 | 15 | - Beyond Locks: Software Transactional Memory: [http://bartoszmilewski.com/2010/09/11/beyond-locks-software-transactional-memory/](http://bartoszmilewski.com/2010/09/11/beyond-locks-software-transactional-memory/) 16 | 17 | - Data Races at the Processor Level: [http://bartoszmilewski.com/2011/08/15/data-races-at-the-processor-level/](http://bartoszmilewski.com/2011/08/15/data-races-at-the-processor-level/) 18 | 19 | - Async Tasks in C++11: Not Quite There Yet: [http://bartoszmilewski.com/2011/10/10/async-tasks-in-c11-not-quite-there-yet/](http://bartoszmilewski.com/2011/10/10/async-tasks-in-c11-not-quite-there-yet/) 20 | 21 | - Race-free Multithreading : Owner polymorphism: [http://bartoszmilewski.com/2009/06/15/race-free-multithreading-owner-polymorphism/](http://bartoszmilewski.com/2009/06/15/race-free-multithreading-owner-polymorphism/) 22 | 23 | - Race-free Multithreading: Ownership: [http://bartoszmilewski.com/2009/06/02/race-free-multithreading-ownership/](http://bartoszmilewski.com/2009/06/02/race-free-multithreading-ownership/) 24 | 25 | - Thin Lock vs. Futex: [http://bartoszmilewski.com/2008/09/01/thin-lock-vs-futex/](http://bartoszmilewski.com/2008/09/01/thin-lock-vs-futex/) 26 | 27 | - Thin Lock Implementation: [http://bartoszmilewski.com/2008/08/16/thin-lock-implementation/](http://bartoszmilewski.com/2008/08/16/thin-lock-implementation/) 28 | 29 | 30 | ## Sutter’s Mill ## 31 | 32 | - atomic Weapons: The C++ Memory Model and Modern Hardware: [http://herbsutter.com/2013/02/11/atomic-weapons-the-c-memory-model-and-modern-hardware/](http://herbsutter.com/2013/02/11/atomic-weapons-the-c-memory-model-and-modern-hardware/) 33 | 34 | ## [Preshing on programming](http://preshing.com/) ## 35 | 36 | - Memory Reordering Caught in the Act: [http://preshing.com/20120515/memory-reordering-caught-in-the-act](http://preshing.com/20120515/memory-reordering-caught-in-the-act) 37 | 38 | - An Introduction to Lock-Free Programming: [http://preshing.com/20120612/an-introduction-to-lock-free-programming](http://preshing.com/20120612/an-introduction-to-lock-free-programming) 39 | 40 | - Memory Ordering at Compile Time: [http://preshing.com/20120625/memory-ordering-at-compile-time](http://preshing.com/20120625/memory-ordering-at-compile-time) 41 | 42 | - Memory Barriers Are Like Source Control Operations: [http://preshing.com/20120710/memory-barriers-are-like-source-control-operations](http://preshing.com/20120710/memory-barriers-are-like-source-control-operations) 43 | 44 | - Acquire and Release Semantics: [http://preshing.com/20120913/acquire-and-release-semantics](http://preshing.com/20120913/acquire-and-release-semantics) 45 | 46 | - Weak vs. Strong Memory Models: [http://preshing.com/20120930/weak-vs-strong-memory-models](http://preshing.com/20120930/weak-vs-strong-memory-models) 47 | 48 | - This Is Why They Call It a Weakly-Ordered CPU: [http://preshing.com/20121019/this-is-why-they-call-it-a-weakly-ordered-cpu](http://preshing.com/20121019/this-is-why-they-call-it-a-weakly-ordered-cpu) 49 | 50 | - Atomic vs. Non-Atomic Operations: [http://preshing.com/20130618/atomic-vs-non-atomic-operations](http://preshing.com/20130618/atomic-vs-non-atomic-operations) 51 | 52 | - The Happens-Before Relation: [http://preshing.com/20130702/the-happens-before-relation](http://preshing.com/20130702/the-happens-before-relation) 53 | 54 | - The Synchronizes-With Relation: [http://preshing.com/20130823/the-synchronizes-with-relation](http://preshing.com/20130823/the-synchronizes-with-relation) 55 | 56 | ## [parallellabs.com](http://www.parallellabs.com) ## 57 | 58 | - 浅析C++多线程内存模型: [http://www.parallellabs.com/2011/08/27/c-plus-plus-memory-model/](http://www.parallellabs.com/2011/08/27/c-plus-plus-memory-model/) 59 | - 为什么程序员需要关心顺序一致性(Sequential Consistency)而不是Cache一致性(Cache Coherence?):[http://www.parallellabs.com/2010/03/06/why-should-programmer-care-about-sequential-consistency-rather-than-cache-coherence/](http://www.parallellabs.com/2010/03/06/why-should-programmer-care-about-sequential-consistency-rather-than-cache-coherence/) 60 | - 多线程程序中操作的原子性: [http://www.parallellabs.com/2010/04/15/atomic-operation-in-multithreaded-application/](http://www.parallellabs.com/2010/04/15/atomic-operation-in-multithreaded-application/) 61 | - 并行编程中的“锁”难题: [http://www.parallellabs.com/2011/10/02/lock-in-parallel-programming/](http://www.parallellabs.com/2011/10/02/lock-in-parallel-programming/) 62 | - Pthreads并行编程之spin lock与mutex性能对比分析: [http://www.parallellabs.com/2010/01/31/pthreads-programming-spin-lock-vs-mutex-performance-analysis/](http://www.parallellabs.com/2010/01/31/pthreads-programming-spin-lock-vs-mutex-performance-analysis/) 63 | - 多核与异步并行: [http://www.parallellabs.com/2013/01/21/multicore-and-asynchronous-communication/](http://www.parallellabs.com/2013/01/21/multicore-and-asynchronous-communication/) 64 | - 多线程队列的算法优化: [http://www.parallellabs.com/2010/10/25/practical-concurrent-queue-algorithm/](http://www.parallellabs.com/2010/10/25/practical-concurrent-queue-algorithm/) 65 | - 剖析为什么在多核多线程程序中要慎用volatile关键字?: [http://www.parallellabs.com/2010/12/04/why-should-we-be-care-of-volatile-keyword-in-multithreaded-applications/](http://www.parallellabs.com/2010/12/04/why-should-we-be-care-of-volatile-keyword-in-multithreaded-applications/) 66 | - 多线程程序常见Bug剖析(上): [http://www.parallellabs.com/2010/11/13/concurrency-bugs-1/](http://www.parallellabs.com/2010/11/13/concurrency-bugs-1/) 67 | - 多线程程序常见Bug剖析(下): [http://www.parallellabs.com/2010/11/23/concurrency-bugs-2/](http://www.parallellabs.com/2010/11/23/concurrency-bugs-2/) 68 | 69 | ## 其他资源 ## 70 | 71 | - Threads and memory model for C++: [http://www.hpl.hp.com/personal/Hans_Boehm/c++mm/](http://www.hpl.hp.com/personal/Hans_Boehm/c++mm/) 72 | 73 | - 内存屏障什么的: [http://www.spongeliu.com/233.html](http://www.spongeliu.com/233.html) 74 | 75 | - 《C++0x漫谈》系列之:多线程内存模型:[http://blog.csdn.net/pongba/article/details/1659952](http://blog.csdn.net/pongba/article/details/1659952) 76 | 77 | - Lock-Free Algorithms: An Introduction: [http://concurrencykit.org/presentations/lockfree_introduction/index.html](http://concurrencykit.org/presentations/lockfree_introduction/index.html) 78 | 79 | - Introduction to Lock-Free Algorithms: Through a case study: [http://concurrencykit.org/presentations/lf1/lf1.pdf 80 | ](http://concurrencykit.org/presentations/lf1/lf1.pdf) 81 | -------------------------------------------------------------------------------- /zh/chapter9-Advanced-Thread-Management/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ewenwan/Cplusplus-Concurrency-In-Practice/8ae68b763434c6babda553d65449e9158d63e357/zh/chapter9-Advanced-Thread-Management/README.md -------------------------------------------------------------------------------- /zh/chapter9-Advanced-Thread-Management/web-resources.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ewenwan/Cplusplus-Concurrency-In-Practice/8ae68b763434c6babda553d65449e9158d63e357/zh/chapter9-Advanced-Thread-Management/web-resources.md --------------------------------------------------------------------------------