├── LICENSE ├── README.md ├── Table-of-contents.md ├── code └── chapter1 │ └── hello-world │ ├── Makefile │ └── hello-world.cc ├── images ├── Screenshot from 2020-07-12 16-40-24.png ├── chapter 3 │ ├── 3.1.1.png │ ├── 3.1.2.png │ ├── 3.1.png │ └── readme.md ├── chapter1 │ ├── concurrent-vs-parallel.png │ ├── fengmian.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 └── stdc++11 │ ├── 1.1.1.jpg │ └── 1.1.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 ├── Python正则表达式操作指南 - Ubuntu中文.pdf ├── README.md ├── c++11奇技淫巧.md ├── pic │ ├── 1.1.1.jpg │ ├── 1.1.png │ └── 5.4.1.png └── 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.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 ├── lock-free.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 | - 原中文版的翻译 一直以来豆瓣 知乎 被嗤之以鼻。刚开始阅读中文版的时候,很多地方,翻译的明显语句不通。不如直接看英文原本。 8 | - 传播知识,介绍 C++ 并发编程。之前有一个《c++并发编程指南》作者 他在github上项目 整理的很好,但是dead 鸽了。原作者@傅海平。我希望接下来一段时间,在原作者的基础上,结合自己的学习,继续整理C++11多线程编程部分。目前我将在基础上添加----->内存模型------->无锁数据结构。 9 | - 自我激励和提高。写该笔记的另一个目的是自我激励和提高。和大部分人一样,本人也是 C++ 菜鸟一枚,在创作本书的过程中我会查阅大量资料,时间长了,自己学到的东西就慢慢积累多了。同时,在写作的时候为了表达清楚和准确,个别语句我会不断推敲,对自己的思维和语言表达能力很有帮助的。最后我坚信: 菜鸟都一样(因为什么都不会),牛人各有各的不同。将之前学习的笔记从word->git。将部分学习敲过的sln中代码取关键细微 的 code移植到git项目当中。 10 | - 自己面试复习以备后续之用。树洞。熟悉md语法和基本git操作。 11 | 12 | >好吧 其实写这么多 也没人看 13 | 14 | ![封面](https://github.com/wshilaji/Cplusplus-Concurrency-In-Action/blob/master/images/chapter1/fengmian.png) 15 | 16 | # 笔记目录(只列出一级目录) # 17 | 18 | ### 第一章 并发编程基础 ### 19 | ### 第二章 几种常见的多线程库介绍 ### 20 | ### [第三章 线程详解](https://github.com/wshilaji/Cplusplus-Concurrency-In-Action/blob/master/zh/chapter3-Thread/README.md) ### 21 | ### [第四章 互斥量与锁](https://github.com/wshilaji/Cplusplus-Concurrency-In-Action/blob/master/zh/chapter4-Mutex/README.md) ### 22 | ### [第五章 条件变量与线程同步](https://github.com/wshilaji/Cplusplus-Concurrency-In-Action/blob/master/zh/chapter5-Condition-Variable/README.md) ### 23 | ### 第六章 异步任务详解 ### 24 | ### 第七章 原子类型详解 ### 25 | ### 第八章 C++11 内存模型 ### 26 | ### 第九章 高级线程管理 ### 27 | ### 第十章 如何编写正确的并发数据结构 ### 28 | ### 第十一章 并发编程应用实例 ### 29 | ### [附录 C++11 新标准概览](https://github.com/wshilaji/Cplusplus-Concurrency-In-Action/blob/master/zh/appendix%20C%2B%2B11%20standards/README.md) ### 30 | 31 | 笔记的详细目录和完成情况请移步[《C++ 并发编程实战》目录](https://github.com/wshilaji/Cplusplus-Concurrency-In-Action/blob/master/Table-of-contents.md) 32 | 33 | # 本书创作发起人 # 34 | 35 | > 36 | 37 | 微博: @当法师不在流浪([https://weibo.com/u/5941342374](https://weibo.com/5941342374)) 38 | 39 | 40 | # 贡献者名单 # 41 | 42 | > 欢迎申请加入该名单 ;-) 43 | 44 | 45 | # License # 46 | 47 | The MIT License (MIT) 48 | 49 | Copyright (c) 2019 杜永森 50 | 51 | Permission is hereby granted, free of charge, to any person obtaining a copy of 52 | this software and associated documentation files (the "Software"), to deal in 53 | the Software without restriction, including without limitation the rights to 54 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 55 | the Software, and to permit persons to whom the Software is furnished to do so, 56 | subject to the following conditions: 57 | 58 | The above copyright notice and this permission notice shall be included in all 59 | copies or substantial portions of the Software. 60 | 61 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 62 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 63 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 64 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 65 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 66 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 67 | -------------------------------------------------------------------------------- /Table-of-contents.md: -------------------------------------------------------------------------------- 1 | # C++ 并发编程指南 # 2 | 本书计划分为 11 章, 分别如下安排: 3 | 4 | ## 第一章 并发编程基础 ## 5 | 6 | ### 1.1 [什么是并发编程](https://github.com/wshilaji/Cplusplus-Concurrency-In-Action/blob/master/zh/chapter1-Introduction/1.1%20What%20is%20concurrency.md) ### 7 | ### 1.2 [并发与并行的区别和联系](https://github.com/wshilaji/Cplusplus-Concurrency-In-Action/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/wshilaji/Cplusplus-Concurrency-In-Action/blob/master/zh/chapter1-Introduction/Cplusplus-Concurrency-Introduction.md) ### 11 | ### 1.6 [资料汇](https://github.com/wshilaji/Cplusplus-Concurrency-In-Action/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/wshilaji/Cplusplus-Concurrency-In-Action/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/wshilaji/Cplusplus-Concurrency-In-Action/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/wshilaji/Cplusplus-Concurrency-In-Action/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/wshilaji/Cplusplus-Concurrency-In-Action/blob/master/zh/chapter3-Thread/web-resources.md) ### 36 | 37 | 38 | ##第四章 互斥量与锁 ## 39 | 40 | ### 4.1 [`` 头文件摘要](https://github.com/wshilaji/Cplusplus-Concurrency-In-Action/blob/master/zh/chapter4-Mutex/4.1%20Mutex-header-synopsis.md) ### 41 | 42 | ### 4.2 [互斥量详解](https://github.com/wshilaji/Cplusplus-Concurrency-In-Action/blob/master/zh/chapter4-Mutex/4.2%20Mutex-tutorial.md#) ### 43 | 44 | ### 4.3 [锁类型详解](https://github.com/wshilaji/Cplusplus-Concurrency-In-Action/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/wshilaji/Cplusplus-Concurrency-In-Action/blob/master/zh/chapter4-Mutex/web-resources.md) ### 51 | 52 | 53 | ##第五章 条件变量与线程同步 ## 54 | 55 | ### 5.1 [`` 头文件摘要](https://github.com/wshilaji/Cplusplus-Concurrency-In-Action/blob/master/zh/chapter5-Condition-Variable/5.1%20Condition-variable%20header%20synopsis.md) ### 56 | 57 | ### 5.2 [条件变量详解](https://github.com/wshilaji/Cplusplus-Concurrency-In-Action/blob/master/zh/chapter5-Condition-Variable/5.2%20Condition-variable-tutorial.md) ### 58 | 59 | ### 5.3 [辅助函数介绍](https://github.com/wshilaji/Cplusplus-Concurrency-In-Action/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/wshilaji/Cplusplus-Concurrency-In-Action/blob/master/zh/chapter5-Condition-Variable/web-resources.md) ### 66 | 67 | 68 | ##第六章 异步任务详解 ## 69 | 70 | ### 6.1 [`` 头文件摘要](https://github.com/wshilaji/Cplusplus-Concurrency-In-Action/blob/master/zh/chapter6-Future/6.1%20Future-header-synopsis.md) ### 71 | 72 | ### 6.2 [异步任务提供者(Provider) 介绍](https://github.com/wshilaji/Cplusplus-Concurrency-In-Action/blob/master/zh/chapter6-Future/6.2%20Providers-tutorial.md) ### 73 | 74 | ### 6.3 [异步任务提供者(Provider) 介绍(续)](https://github.com/wshilaji/Cplusplus-Concurrency-In-Action/blob/master/zh/chapter6-Future/6.3%20Providers-tutorial-2.md) ### 75 | 76 | ### 6.4 [异步任务 Future 类型详解](https://github.com/wshilaji/Cplusplus-Concurrency-In-Action/blob/master/zh/chapter6-Future/6.4%20Future-tutorial.md) ### 77 | 78 | ### 6.5 [与异步任务相关的类型介绍](https://github.com/wshilaji/Cplusplus-Concurrency-In-Action/blob/master/zh/chapter6-Future/6.5%20Auxiliary-types.md) ### 79 | 80 | ### 6.6 [异步任务辅助函数 `std::async` 介绍](https://github.com/wshilaji/Cplusplus-Concurrency-In-Action/blob/master/zh/chapter6-Future/6.6%20Auxiliary-function.md) ### 81 | 82 | ### 6.7 [异步任务与多线程实例](https://github.com/wshilaji/Cplusplus-Concurrency-In-Action/blob/master/zh/chapter6-Future/6.7%20Future-multithreading-application.md) ### 83 | 84 | ### 6.8 [资料汇](https://github.com/wshilaji/Cplusplus-Concurrency-In-Action/blob/master/zh/chapter6-Future/web-resources.md) ### 85 | 86 | 87 | ##第七章 原子类型详解 ## 88 | 89 | ### 7.1 [`` 头文件摘要](https://github.com/wshilaji/Cplusplus-Concurrency-In-Action/blob/master/zh/chapter7-Atomic/7.1%20Atomic-header-synopsis.md) ### 90 | 91 | ### 7.2 [`std::atomic_flag` 详解](https://github.com/wshilaji/Cplusplus-Concurrency-In-Action/blob/master/zh/chapter7-Atomic/7.2%20Atomic-flag-tutorial.md) ### 92 | 93 | ### 7.3 [基本 `std::atomic` 类型详解](https://github.com/wshilaji/Cplusplus-Concurrency-In-Action/blob/master/zh/chapter7-Atomic/7.3%20Atomic-tutorial.md) ### 94 | 95 | ### 7.4 [特化的 `std::atomic` 类型详解](https://github.com/wshilaji/Cplusplus-Concurrency-In-Action/blob/master/zh/chapter7-Atomic/7.4%20Atomic-tutorial2.md) ### 96 | 97 | ### 7.5 [C 风格的原子操作](https://github.com/wshilaji/Cplusplus-Concurrency-In-Action/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/wshilaji/Cplusplus-Concurrency-In-Action/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/wshilaji/Cplusplus-Concurrency-In-Action/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/wshilaji/Cplusplus-Concurrency-In-Action/blob/master/zh/chapter11-Application/11.1%20Producer-Consumer-solution.md) ### 125 | 126 | 127 | ##附录 C++11 新标准概览 ## 128 | 129 | ### 1. [核心语言的运行时性能强化](https://github.com/wshilaji/Cplusplus-Concurrency-In-Action/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/wshilaji/Cplusplus-Concurrency-In-Action/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/wshilaji/Cplusplus-Concurrency-In-Action/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. 用于计算函数对象返回类型的统一方法 193 | 12. std::chrono库详解 194 | -------------------------------------------------------------------------------- /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/Screenshot from 2020-07-12 16-40-24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wshilaji/Cplusplus-Concurrency-In-Action/dc0ec9c42abc7b0fddf03e6289e7cde1f6ff3117/images/Screenshot from 2020-07-12 16-40-24.png -------------------------------------------------------------------------------- /images/chapter 3/3.1.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wshilaji/Cplusplus-Concurrency-In-Action/dc0ec9c42abc7b0fddf03e6289e7cde1f6ff3117/images/chapter 3/3.1.1.png -------------------------------------------------------------------------------- /images/chapter 3/3.1.2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wshilaji/Cplusplus-Concurrency-In-Action/dc0ec9c42abc7b0fddf03e6289e7cde1f6ff3117/images/chapter 3/3.1.2.png -------------------------------------------------------------------------------- /images/chapter 3/3.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wshilaji/Cplusplus-Concurrency-In-Action/dc0ec9c42abc7b0fddf03e6289e7cde1f6ff3117/images/chapter 3/3.1.png -------------------------------------------------------------------------------- /images/chapter 3/readme.md: -------------------------------------------------------------------------------- 1 | save pics 2 | -------------------------------------------------------------------------------- /images/chapter1/concurrent-vs-parallel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wshilaji/Cplusplus-Concurrency-In-Action/dc0ec9c42abc7b0fddf03e6289e7cde1f6ff3117/images/chapter1/concurrent-vs-parallel.png -------------------------------------------------------------------------------- /images/chapter1/fengmian.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wshilaji/Cplusplus-Concurrency-In-Action/dc0ec9c42abc7b0fddf03e6289e7cde1f6ff3117/images/chapter1/fengmian.png -------------------------------------------------------------------------------- /images/chapter1/intel-blog-concurrency.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wshilaji/Cplusplus-Concurrency-In-Action/dc0ec9c42abc7b0fddf03e6289e7cde1f6ff3117/images/chapter1/intel-blog-concurrency.png -------------------------------------------------------------------------------- /images/chapter1/intel-blog-parallel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wshilaji/Cplusplus-Concurrency-In-Action/dc0ec9c42abc7b0fddf03e6289e7cde1f6ff3117/images/chapter1/intel-blog-parallel.png -------------------------------------------------------------------------------- /images/chapter1/process-memory-layout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wshilaji/Cplusplus-Concurrency-In-Action/dc0ec9c42abc7b0fddf03e6289e7cde1f6ff3117/images/chapter1/process-memory-layout.png -------------------------------------------------------------------------------- /images/chapter1/program-memory-layout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wshilaji/Cplusplus-Concurrency-In-Action/dc0ec9c42abc7b0fddf03e6289e7cde1f6ff3117/images/chapter1/program-memory-layout.png -------------------------------------------------------------------------------- /images/chapter8/its-lock-free.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wshilaji/Cplusplus-Concurrency-In-Action/dc0ec9c42abc7b0fddf03e6289e7cde1f6ff3117/images/chapter8/its-lock-free.png -------------------------------------------------------------------------------- /images/chapter8/lock-free-intro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wshilaji/Cplusplus-Concurrency-In-Action/dc0ec9c42abc7b0fddf03e6289e7cde1f6ff3117/images/chapter8/lock-free-intro.png -------------------------------------------------------------------------------- /images/chapter9/object-layout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wshilaji/Cplusplus-Concurrency-In-Action/dc0ec9c42abc7b0fddf03e6289e7cde1f6ff3117/images/chapter9/object-layout.png -------------------------------------------------------------------------------- /images/stdc++11/1.1.1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wshilaji/Cplusplus-Concurrency-In-Action/dc0ec9c42abc7b0fddf03e6289e7cde1f6ff3117/images/stdc++11/1.1.1.jpg -------------------------------------------------------------------------------- /images/stdc++11/1.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wshilaji/Cplusplus-Concurrency-In-Action/dc0ec9c42abc7b0fddf03e6289e7cde1f6ff3117/images/stdc++11/1.1.png -------------------------------------------------------------------------------- /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: -------------------------------------------------------------------------------- 1 | ## __4. C++ 标准库的变更__ ## 2 | 3 | ### 4.1 变长参数模板 ### 4 | 5 | ### 4.2 新的字符串字面值 ### 6 | 7 | ### 4.3 用户自定义的字面值 ### 8 | 9 | ### 4.4 多线程内存模型 ### 10 | 11 | ### 4.5 线程本地存储 ### 12 | 13 | ### 4.6 特殊成员函数 ### 14 | 15 | 一个类,只有数据成员时 16 | 17 | class DataOnly { 18 | private: 19 | int data_; 20 | }; 21 | 22 | C++98 编译器会隐式的产生四个函数:缺省构造函数,析构函数,拷贝构造函数 和 拷贝赋值算子,它们称为特殊成员函数 (special member function) 在 C++11 中,除了上面四个外,特殊成员函数还有两个:移动构造函数 和 移动赋值算子。 23 | 24 | class DataOnly { 25 | public: 26 | DataOnly () // default constructor 27 | ~DataOnly () // destructor 28 | DataOnly (const DataOnly & rhs)     // copy constructor 29 | DataOnly & operator=(const DataOnly & rhs) // copy assignment operator 30 | DataOnly (const DataOnly && rhs) // C++11, move constructor 31 | DataOnly & operator=(DataOnly && rhs) // C++11, move assignment operator 32 | }; 33 | 34 | 35 | ### 4.7 显式地使用或禁用某些特殊成员函数(构造函数,拷贝构造,赋值操作符,析构等) ### 36 | 37 | #### final 说明符 #### 38 | 39 | 可以使用最终关键字来指定不能在派生类中重写的虚函数。 您还可以使用它指定无法继承的类。下面的示例使用最终关键字来指定,不能重写虚函数。 40 | 41 | class BaseClass 42 | { 43 | virtual void func() final; 44 | }; 45 | class DerivedClass: public BaseClass 46 | { 47 | virtual void func(); // compiler error: attempting to 48 | // override a final function 49 | }; 50 | 51 | 下面的示例使用最终关键字指定无法继承类。 52 | 53 | class BaseClass final{ }; 54 | class DerivedClass: public BaseClass // compiler error: BaseClass is 55 | // marked as non-inheritable 56 | { 57 | }; 58 | 59 | #### override 说明符 #### 60 | 61 | 可以使用重写关键字来指定重写基类中的虚函数的函数的成员。使用重写以帮助防止在代码中的意外的继承行为。 下面的示例演示的位置,而无需使用重写,派生类的成员函数的行为可能不适用。编译器不会发出此代码的任何错误。 62 | 63 | class BaseClass 64 | { 65 | virtual void funcA(); 66 | virtual void funcB() const; 67 | virtual void funcC(int = 0); 68 | void funcD(); 69 | }; 70 | class DerivedClass: public BaseClass 71 | { 72 | virtual void funcA(); // ok, works as intended 73 | virtual void funcB(); // DerivedClass::funcB() is non-const, so it does not 74 | // override BaseClass::funcB() const and it is a new member function 75 | virtual void funcC(double = 0.0); // DerivedClass::funcC(double) has a different 76 | // parameter type than BaseClass::funcC(int), so 77 | // DerivedClass::funcC(double) is a new member function 78 | }; 79 | 80 | 当你使用重写,编译器将生成错误而不是以无提示方式创建新的成员函数。 81 | 82 | class BaseClass 83 | { 84 | virtual void funcA(); 85 | virtual void funcB() const; 86 | virtual void funcC(int = 0); 87 | void funcD(); 88 | }; 89 | class DerivedClass: public BaseClass 90 | { 91 | virtual void funcA() override; // ok 92 | virtual void funcB() override; // compiler error: DerivedClass::funcB() does not 93 | // override BaseClass::funcB() const 94 | virtual void funcC( double = 0.0 ) override; // compiler error: 95 | // DerivedClass::funcC(double) does not 96 | // override BaseClass::funcC(int) 97 | void funcD() override; // compiler error: DerivedClass::funcD() does not 98 | // override the non-virtual BaseClass::funcD() 99 | }; 100 | 101 | 若要指定函数不能重写和不能继承的类,请使用最终关键字。 102 | 103 | #### 显式默认设置的函数和已删除的函数 #### 104 | 105 | 在 C++11 中,默认函数和已删除函数使你可以显式控制是否自动生成特殊成员函数。 已删除的函数还可为您提供简单语言,以防止所有类型的函数(特殊成员函数和普通成员函数以及非成员函数)的参数中出现有问题的类型提升,这会导致意外的函数调用。 106 | __显式默认设置的函数和已删除函数的好处__ 107 | 在 C++ 中,如果某个类型未声明它本身,则编译器将自动为该类型生成默认构造函数、复制构造函数、复制赋值运算符和析构函数。这些函数称为特殊成员函数,并且它们是什么发出简单用户定义的类型在 C++ 行为方式就类似结构往来 c。也就是说,你可以创建、 复制和销毁它们而无需任何额外编码工作。 C++11 会将移动语义引入语言中,并将移动构造函数和移动赋值运算符添加到编译器可自动生成的特殊成员函数的列表中。 108 | 这对于简单类型非常方便,但是复杂类型通常自己定义一个或多个特殊成员函数,这可以阻止自动生成其他特殊成员函数。 实践操作: 109 | * 如果显式声明了任何构造函数,则不会自动生成默认构造函数。 110 | * 如果显式声明了虚拟析构函数,则不会自动生成默认析构函数。 111 | * 如果显式声明了移动构造函数或移动赋值运算符,则: 112 | + 不自动生成复制构造函数。 113 | + 不自动生成复制赋值运算符。 114 | * 如果显式声明了复制构造函数、复制赋值运算符、移动构造函数、移动赋值运算符或析构函数,则: 115 | + 不自动生成移动构造函数。 116 | + 不自动生成移动赋值运算符。 117 | 118 | 备注:此外,C++11 标准指定将以下附加规则: 119 | * 如果显式声明了复制构造函数或析构函数,则弃用复制赋值运算符的自动生成。 120 | * 如果显式声明了复制赋值运算符或析构函数,则弃用复制构造函数的自动生成。 121 | 在这两种情况下,Visual Studio 将继续隐式自动生成所需的函数且不发出警告。这些规则的结果也可能泄漏到对象层次结构中。 例如,如果出于任何原因基类不具有默认构造函数可从派生类调用 — 即,公共或受保护的不带参数的构造函数 — 然后类派生不能自动生成其自己的默认构造函数。 122 | 123 | 这些规则可能会使本应直接的内容、用户定义类型和常见 C++ 惯例的实现变得复杂 — 例如,通过以私有方式复制构造函数和复制赋值运算符,而不定义它们,使用户定义类型不可复制。 124 | 125 | 126 | 作为开发方,如果不想让用户使用某个成员函数,不声明即可;但对于特殊成员函数,则是另一种情况。例如,设计一个树叶类 127 | 128 | class LeafOfTree{ ... }; 129 | 莱布尼茨说过,“世上没有两片完全相同的树叶” (Es gibt keine zwei Blätter, die gleich bleiben),因此,对于一片独一无二的树叶,下面的操作是错误的: 130 | 131 | LeafOfTree leaf1; 132 | LeafOfTree leaf2; 133 | LeafOfTree leaf3(leaf1); // attempt to copy Leaf1 — should not compile! 134 | Leaf1 = Leaf2; // attempt to copy Leaf2 — should not compile! 135 | 136 | 因此,此时需要避免使用 “拷贝构造函数” 和 “拷贝赋值算子” 137 | 138 | ~~C++98 中,可声明这些特殊成员函数为私有型 (private),且不实现该函数,具体如下:~~ 139 | 140 | class LeafOfTree{ 141 | private: 142 | LeafOfTree( const LeafOfTree& );        // not defined 143 | LeafOfTree & operator=( const LeafOfTree& ); // not defined 144 | }; 145 | 146 | 程序中如果调用了 LeafOfTree 类的拷贝构造函数 (或拷贝赋值操作符),则在编译时,会出现链接错误 (link-time error) 147 | 为了将报错提前到编译时 (compile time),可增加了一个基类 Uncopyable,并将拷贝构造函数和拷贝赋值算子声明为私有型,具体可参见 《Effective C++_3rd item 6》 在谷歌 C++ 编码规范中,使用了一个宏定义来简化,如下所示: 148 | 149 | // A macro to disallow the copy constructor and operator= functions 150 | // This should be used in the priavte:declarations for a class 151 | #define DISALLOW_COPY_AND_ASSIGN(TypeName) \ 152 | TypeName(const TypeName&); \ 153 | TypeName& operator=(const TypeName&) 154 | 155 | 在 C++11 之前,此代码段是不可复制的类型的惯例形式。 但是,它具有几个问题: 156 | * 复制构造函数必须以私有方式进行声明以隐藏它,但因为它进行了完全声明,所以会阻止自动生成默认构造函数。 如果你需要默认构造函数,则必须显式定义一个(即使它不执行任何操作)。 157 | * 即使显式定义的默认构造函数不执行任何操作,编译器也会将它视为重要内容。 其效率低于自动生成的默认构造函数,并且会阻止 noncopyable 成为真正的 POD 类型。 158 | * 尽管复制构造函数和复制赋值运算符在外部代码中是隐藏的,但成员函数和 noncopyable 的友元仍可以看见并调用它们。 如果它们进行了声明但是未定义,则调用它们会导致链接器错误。 159 | * 虽然这是广为接受的惯例,但是除非你了解用于自动生成特殊成员函数的所有规则,否则意图不明确。在 C++11 中,不可复制的习语可通过更直接的方法实现。 160 | 161 | struct LeafOfTree 162 | { 163 | LeafOfTree() =default; 164 | LeafOfTree( const LeafOfTree& )=delete; 165 | LeafOfTree & operator=( const LeafOfTree& )=delete; 166 | }; 167 | 168 | 请注意如何解决与 C++11 之前的惯例有关的问题: 169 | * 仍可通过声明复制构造函数来阻止生成默认构造函数,但可通过将其显式设置为默认值进行恢复。 170 | * 显式设置的默认特殊成员函数仍被视为不重要的,因此性能不会下降,并且不会阻止 noncopyable 成为真正的 POD 类型。 171 | * 复制构造函数和复制赋值运算符是公共的,但是已删除。 定义或调用已删除函数是编译时错误。 172 | * 对于了解 =default 和 =delete 的人来说,意图是非常清楚的。 你不必了解用于自动生成特殊成员函数的规则。 173 | 对于创建不可移动、只能动态分配或无法动态分配的用户定义类型,存在类似惯例。 所有这些惯例都具有 C++11 之前的实现,这些实现会遭受类似问题,并且可在 C++11 中通过按照默认和已删除特殊成员函数实现它们,以类似方式进行解决。 174 | 175 | #### 显式默认设置的函数 = default函数 #### 176 | 177 | 因为一旦你自己定义了构造函数,或其他构造函数,默认构造函数就不再提供但是有的时候,比如你定义了class A{ A(int a){}}这个有参构造函数,当你在主函数中使用时,必须A a(10)这样来初始化,但是你想只定义A a;这个时候,调用无参构造函数,然后系统不给提供默认构造的了,你必须自己定义一个无参的构造函数,但是你只写了A(){},函数体内部你不想写,将这个函数声明成"=default"函数即可,函数体不用写了,变成了原本不会提供的默认构造函数了。 178 | 179 | class X{ 180 | public: 181 | X() = default; //该函数比用户自己定义的默认构造函数获得更高的代码效率 182 | X(int i){a = i;} 183 | private: 184 | int a; 185 | }; 186 | X obj; 187 | // "=default"函数特性仅适用于类的特殊成员函数,且该特殊成员函数没有默认参数。 188 | class X1{ 189 | public: 190 | int f() = default; // err , 函数 f() 非类 X 的特殊成员函数 191 | X1(int, int) = default; // err , 构造函数 X1(int, int) 非 X 的特殊成员函数 192 | X1(int = 1) = default; // err , 默认构造函数 X1(int=1) 含有默认参数 193 | }; 194 | // "=default"函数既可以在类体里(inline)定义,也可以在类体外(out-of-line)定义。 195 | class X2{ 196 | public: 197 | X2() = default; //Inline defaulted 默认构造函数 198 | X2(const X&); 199 | X2& operator = (const X&); 200 | ~X2() = default; //Inline defaulted 析构函数 201 | }; 202 | X2::X2(const X&) = default; //Out-of-line defaulted 拷贝构造函数 203 | X2& X2::operator= (const X2&) = default; //Out-of-line defaulted 拷贝赋值操作符 204 | 205 | 可以默认设置任何特殊成员函数 — 以显式声明特殊成员函数使用默认实现、定义具有非公共访问限定符的特殊成员函数或恢复其他情况下被阻止其自动生成的特殊成员函数。可通过如此示例所示进行声明来默认设置特殊成员函数: 206 | 207 | struct widget 208 | { 209 | widget()=default; 210 | inline widget& operator=(const widget&); 211 | }; 212 | inline widget& widget::operator=(const widget&) =default; 213 | 214 | 请注意,只要特殊成员函数可内联,便可以在类主体外部默认设置它。由于普通特殊成员函数的性能优势,因此我们建议你在需要默认行为时首选自动生成的特殊成员函数而不是空函数体。 你可以通过显式默认设置特殊成员函数,或通过不声明它(也不声明其他会阻止它自动生成的特殊成员函数),来实现此目的。 215 | 216 | #### 已删除的函数 #### 217 | 218 | 可以删除特殊成员函数以及普通成员函数和非成员函数,以阻止定义或调用它们。 通过删除特殊成员函数,可以更简洁地阻止编译器生成不需要的特殊成员函数。 必须在声明函数时将其删除;不能在这之后通过声明一个函数然后不再使用的方式来将其删除。 219 | 220 | struct widget 221 | { 222 | // deleted operator new prevents widget from being dynamically allocated. 223 | void* operator new(std::size_t) = delete; 224 | }; 225 | 226 | 删除普通成员函数或非成员函数可阻止有问题的类型提升导致调用意外函数。 这可发挥作用的原因是,已删除的函数仍参与重载决策,并提供比提升类型之后可能调用的函数更好的匹配。 函数调用将解析为更具体的但可删除的函数,并会导致编译器错误。 227 | 228 | // deleted overload prevents call through type promotion of float to double from succeeding. 229 | void call_with_true_double_only(float) =delete; 230 | void call_with_true_double_only(double param) { return; } 231 | template < typename T > 232 | void call_with_true_double_only(T) =delete; //prevent call through type promotion of any T to double from succeeding. 233 | void call_with_true_double_only(double param) { return; } // also define for const double, double&, etc. as needed. 234 | 235 | 请注意,在上述示例中,调用call_with_true_double_only通过使用 __float__ 参数会导致编译器错误,但调用call_with_true_double_only通过 __int__ 参数不会; 在 __int__ 的情况下,该参数将从提升 __int__ 到 __double__ 并成功调用 __double__ 版本的函数即使这不可能的用途是什么。 若要确保使用非双精度自变量对此函数进行的任何调用均会导致编译器错误,你可以声明已删除的函数的模板版本。 236 | 237 | delete在模板特化。在模板特例化中,也可以用 delete 来过滤一些特定的形参类型。例如,Widget 类中声明了一个模板函数,当进行模板特化时,要求禁止参数为 void* 的函数调用。如果按照 C++98 的 “私有不实现“ 思路,应该是将特例化的函数声明为私有型,如下所示: 238 | 239 | class Widget { 240 | public: 241 | template 242 | void ProcessPointer(T* ptr) { … } 243 | private: 244 | template<> 245 | void ProcessPointer(void*); // error! 246 | }; 247 | 248 | 问题是,模板特化应该被写在命名空间域 (namespace scope),而不是类域 (class scope),因此,该方法会报错。而在 C++11 中,因为有了 delete 关键字,则可以直接在类域外,将特例化的模板函数声明为 delete, 如下所示: 249 | 250 | class Widget { 251 | public: 252 | template 253 | void ProcessPointer(T* ptr) { … } 254 | }; 255 | template<> 256 | void Widget::ProcessPointer(void*) = delete; // still public, but deleted 257 | 258 | ### 4.7 long long int类型 ### 259 | 260 | ### 4.8 继承构造/委托构造 ### 261 | 262 | 继承构造。C++ 11允许派生类继承基类的构造函数(默认构造函数、复制构造函数、移动构造函数除外)。注意:继承的构造函数只能初始化基类中的成员变量,不能初始化派生类的成员变量。如果基类的构造函数被声明为私有,或者派生类是从基类中虚继承,那么不能继承构造函数。一旦使用继承构造函数,编译器不会再为派生类生成默认构造函数 263 | 264 | class A 265 | { 266 | public: 267 | A(int i) { std::cout << "i = " << i << std::endl; } 268 | A(double d, int i) {} 269 | A(float f, int i, const char* c) {} 270 | // ... 271 | }; 272 | class B : public A 273 | { 274 | public: 275 | using A::A; // 继承构造函数 276 | // ... 277 | virtual void ExtraInterface(){} 278 | }; 279 | 280 | 委托构造和继承构造函数类似,委托构造函数也是C++11中对C++的构造函数的一项改进,其目的也是为了减少程序员书写构造函数的时间。// 如果一个类包含多个构造函数,C++ 11允许在一个构造函数中的定义中使用另一个构造函数,但这必须通过初始化列表进行操作,如下: 281 | 282 | class Info 283 | { 284 | public: 285 | Info() : Info(1) { } // 委托构造函数 286 | Info(int i) : Info(i, 'a') { } // 既是目标构造函数,也是委托构造函数 287 | Info(char e): Info(1, e) { } 288 | private: 289 | Info(int i, char e): type(i), name(e) { /\* 其它初始化 \*/} // 目标构造函数 290 | int type; 291 | char name; 292 | // ... 293 | }; 294 | 295 | ### 4.9 静态断言 assertions ### 296 | 297 | ### 4.10 允许 sizeof 运算符作用在类型的数据成员上,无须明确的对象 ### 298 | 299 | ### 4.11 垃圾回收机制 ### 300 | 301 | ### 4.12 属性 ### 302 | 303 | 304 | -------------------------------------------------------------------------------- /zh/appendix C++11 standards/Python正则表达式操作指南 - Ubuntu中文.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wshilaji/Cplusplus-Concurrency-In-Action/dc0ec9c42abc7b0fddf03e6289e7cde1f6ff3117/zh/appendix C++11 standards/Python正则表达式操作指南 - Ubuntu中文.pdf -------------------------------------------------------------------------------- /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/wshilaji/Cplusplus-Concurrency-In-Action/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. static 45 | 4. 对 POD 类型定义的修正。 46 | 47 | ## 2. [核心语言的构建时性能强化](https://github.com/wshilaji/Cplusplus-Concurrency-In-Action/blob/master/zh/appendix%20C%2B%2B11%20standards/C%2B%2B11%20Core%20language%20build%20time%20performance%20enhancements.md) ## 48 | 49 | 本小节主要包括: 50 | 51 | 1. 外部模板。 52 | 53 | ## 3. [核心语言的可用性强化](https://github.com/wshilaji/Cplusplus-Concurrency-In-Action/blob/master/zh/appendix%20C%2B%2B11%20standards/C%2B%2B11%20Core%20language%20usability%20enhancements.md) ## 54 | 55 | 本小节主要包括: 56 | 57 | 1. 初始化列表(`std::initializer_list`) 58 | 2. 统一的初始化方式 59 | 3. 类型推导(auto 和 decltype 关键字) 60 | 4. 基于范围的 for 循环 61 | 5. lambda 表达式 62 | 6. 另一种可选的函数语法 63 | 7. 对象创建优化 64 | 8. 显式虚函数重载 65 | 9. 空指针常量(nullptr) 66 | 10. 强类型枚举 67 | 11. 右尖括号(>) 68 | 12. 显式类型转换操作符 69 | 13. 模板别名 70 | 14. 无限制 union。 71 | 15. [std::function<>](https://github.com/wshilaji/Cplusplus-Concurrency-In-Action/blob/master/zh/appendix%20C++11%20standards/C++11%20Core%20language%20usability%20enhancements.md#315-stdfuction) 72 | 16. [std::bind<>](https://github.com/wshilaji/Cplusplus-Concurrency-In-Action/blob/master/zh/appendix%20C++11%20standards/C++11%20Core%20language%20usability%20enhancements.md#316-stdbind) 73 | ## 4. [核心语言的功能提升](https://github.com/wshilaji/Cplusplus-Concurrency-In-Action/blob/master/zh/appendix%20C++11%20standards/C++11%20Core%20language%20functionality%20improvements.md) ## 74 | 75 | 本小节主要包括: 76 | 77 | 1. 变长参数模板 78 | 2. 新的字符串字面值 79 | 3. 用户自定义的字面值 80 | 4. 多线程内存模型 81 | 5. 线程本地存储 82 | 6. 显式地使用或禁用某些特殊成员函数(构造函数,拷贝构造,赋值操作符,析构等) 83 | 7. long long int类型 84 | 8. 继承构造/委托构造 85 | 9. 静态断言 assertions 86 | 10. 允许 sizeof 运算符作用在类型的数据成员上,无须明确的对象 87 | 11. 垃圾回收机制 88 | 12. 属性 89 | 90 | ## 5. [C++ 标准库的变更](https://github.com/wshilaji/Cplusplus-Concurrency-In-Action/blob/master/zh/appendix%20C%2B%2B11%20standards/C%2B%2B11%20C%2B%2B%20standard%20library%20changes.md) ## 91 | 92 | 本小节主要包括: 93 | 94 | 1. 标准库组件上的升级 95 | 2. 多线程支持 96 | 3. 元组(tuple)类型 97 | 4. 散列表(hash table) 98 | 5. 正则表达式 99 | 6. 通用智能指针 100 | 7. 可扩展的随机数功能 101 | 8. 包装引用 102 | 9. 多态函数对象包装器 103 | 10. 用于元编程的类型属性 104 | 11. 用于计算函数对象返回类型的统一方法 105 | 12. std::chrono库详解 106 | 107 | ## 6.[C++ 奇淫巧技](https://github.com/wshilaji/Cplusplus-Concurrency-In-Action/blob/master/zh/appendix%20C%2B%2B11%20standards/c%2B%2B11%E5%A5%87%E6%8A%80%E6%B7%AB%E5%B7%A7.md) ## 108 | 109 | 本小节主要包括: 110 | 111 | 1. SFINAE技术 112 | 2. 私有成员函数一定不能被外部调用吗? 113 | 3. 模板元编程 114 | 4. 115 | -------------------------------------------------------------------------------- /zh/appendix C++11 standards/c++11奇技淫巧.md: -------------------------------------------------------------------------------- 1 | ## 1. SFINAE ## 2 | SFINAE 技术,即匹配失败不是错误,英文Substitution Failure Is Not An Error,其作用是当我们在进行模板特化的时候,会去选择那个正确的模板,避免失败 3 | 4 | 5 | template 6 | struct is_pointer 7 | { 8 | template 9 | static char is_ptr(U *); 10 | 11 | template 12 | static char is_ptr(Y X::*); 13 | 14 | template 15 | static char is_ptr(U(*)()); 16 | 17 | static double is_ptr(...); 18 | 19 | static T t; 20 | enum { value = sizeof(is_ptr(t)) == sizeof(char) };// 是否sizeof(is_ptr(t)) == sizeof(char) 就是前者是否==1,如果为真 返回1 假返回0 21 | // 传进来 IntPtr; ===>static T t ====>static int * t======>is_ptr(t) 函数重载 选择类型 t相对应的类型 22 | }; 23 | 24 | struct Foo { 25 | int bar; 26 | }; 27 | 28 | void testTypeCheck() { 29 | typedef int * IntPtr; 30 | typedef int Foo::* FooMemberPtr; //这个并不是结构体指针 而是 结构体里面 int bar的指针 31 | typedef int(*FuncPtr)(); 32 | 33 | printf("%d\n", is_pointer::value); // prints 1 34 | printf("%d\n", is_pointer::value); // prints 1 35 | printf("%d\n", is_pointer::value); // prints 1 36 | } 37 | 38 | // 接着我们来看下 muduo 库中的一段代码: 39 | template 40 | struct has_no_destroy 41 | { 42 | template 43 | static char test(decltype(&C::no_destroy)); 44 | 45 | 46 | template 47 | static int32_t test(...); 48 | 49 | const static bool value = sizeof(test(0)) == 1;//sizeof(类型) 返回类型大小 50 | // 就是前者是否 == 1,如果为真 返回1 假返回0 51 | // (int)&(((AdvTeacher *)0)->age);//0就是这个结构体变量,强制类型转化成AdvTeacher指针 把结构体映射到 null位置 52 | //这里 A结构体类型 函数重载选择 int32_t 4 53 | //这里 B结构体类型 函数重载选择 char 1 54 | }; 55 | // 其作用就是用来判断是否有 no_destroy 函数 56 | 57 | struct A 58 | { 59 | 60 | }; 61 | 62 | struct B { 63 | void no_destroy() {} 64 | }; 65 | struct C { 66 | int no_destroy; 67 | }; 68 | 69 | struct D : B { 70 | 71 | }; 72 | 73 | void testNoDestroy() { 74 | printf("%d\n", has_no_destroy::value); 75 | printf("%d\n", has_no_destroy::value); 76 | printf("%d\n", has_no_destroy::value); 77 | printf("%d\n", has_no_destroy::value); 78 | /* 79 | 0 80 | 1 81 | 1 82 | 1 83 | */ 84 | } 85 | int main89446() 86 | { 87 | testTypeCheck(); 88 | testNoDestroy(); 89 | return 0; 90 | } 91 | -------------------------------------------------------------------------------- /zh/appendix C++11 standards/pic/1.1.1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wshilaji/Cplusplus-Concurrency-In-Action/dc0ec9c42abc7b0fddf03e6289e7cde1f6ff3117/zh/appendix C++11 standards/pic/1.1.1.jpg -------------------------------------------------------------------------------- /zh/appendix C++11 standards/pic/1.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wshilaji/Cplusplus-Concurrency-In-Action/dc0ec9c42abc7b0fddf03e6289e7cde1f6ff3117/zh/appendix C++11 standards/pic/1.1.png -------------------------------------------------------------------------------- /zh/appendix C++11 standards/pic/5.4.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wshilaji/Cplusplus-Concurrency-In-Action/dc0ec9c42abc7b0fddf03e6289e7cde1f6ff3117/zh/appendix C++11 standards/pic/5.4.1.png -------------------------------------------------------------------------------- /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/wshilaji/Cplusplus-Concurrency-In-Action/blob/master/zh/chapter1-Introduction/1.1%20What%20is%20concurrency.md) ## 14 | ## 1.2 [并发与并行的区别和联系](https://github.com/wshilaji/Cplusplus-Concurrency-In-Action/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/wshilaji/Cplusplus-Concurrency-In-Action/blob/master/zh/chapter1-Introduction/Cplusplus-Concurrency-Introduction.md) ## 18 | ## 1.6 [资料汇](https://github.com/wshilaji/Cplusplus-Concurrency-In-Action/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/wshilaji/Cplusplus-Concurrency-In-Action/dc0ec9c42abc7b0fddf03e6289e7cde1f6ff3117/zh/chapter10-Concurrent-Data-Structure/README.md -------------------------------------------------------------------------------- /zh/chapter10-Concurrent-Data-Structure/web-resources.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wshilaji/Cplusplus-Concurrency-In-Action/dc0ec9c42abc7b0fddf03e6289e7cde1f6ff3117/zh/chapter10-Concurrent-Data-Structure/web-resources.md -------------------------------------------------------------------------------- /zh/chapter11-Application/web-resources.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wshilaji/Cplusplus-Concurrency-In-Action/dc0ec9c42abc7b0fddf03e6289e7cde1f6ff3117/zh/chapter11-Application/web-resources.md -------------------------------------------------------------------------------- /zh/chapter2-Thread-Libraries/2.1 Pthread.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wshilaji/Cplusplus-Concurrency-In-Action/dc0ec9c42abc7b0fddf03e6289e7cde1f6ff3117/zh/chapter2-Thread-Libraries/2.1 Pthread.md -------------------------------------------------------------------------------- /zh/chapter2-Thread-Libraries/2.2 Windows multithreading.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wshilaji/Cplusplus-Concurrency-In-Action/dc0ec9c42abc7b0fddf03e6289e7cde1f6ff3117/zh/chapter2-Thread-Libraries/2.2 Windows multithreading.md -------------------------------------------------------------------------------- /zh/chapter2-Thread-Libraries/2.3 Multithreading-libraries-comparison.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wshilaji/Cplusplus-Concurrency-In-Action/dc0ec9c42abc7b0fddf03e6289e7cde1f6ff3117/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/README.md: -------------------------------------------------------------------------------- 1 | # 本章导读 # 2 | 3 | 本章主要内容如下: 4 | 5 | - `` 头文件中的类及相关函数的介绍,包括创建新的线程对象、线程对象的控制和管理、与线程相关的异常处理等。 6 | - `std::thread` 与 Pthread 等线程库的对比,包括两者编程接口的差异及其他细节。 7 | - `std::thread` 应用实例。 8 | 9 | # 第三章 线程详解 # 10 | 11 | 12 | ## [3.1 `` 头文件摘要](https://github.com/wshilaji/Cplusplus-Concurrency-In-Action/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/wshilaji/Cplusplus-Concurrency-In-Action/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/wshilaji/Cplusplus-Concurrency-In-Action/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/wshilaji/Cplusplus-Concurrency-In-Action/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/wshilaji/Cplusplus-Concurrency-In-Action/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/wshilaji/Cplusplus-Concurrency-In-Action/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/wshilaji/Cplusplus-Concurrency-In-Action/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/wshilaji/Cplusplus-Concurrency-In-Action/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/wshilaji/Cplusplus-Concurrency-In-Action/blob/master/zh/chapter3-Thread/web-resources.md) ## 26 | -------------------------------------------------------------------------------- /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. (默认) 无 请求锁,阻塞当前线程直到成功获得锁。 21 | 3. std::defer_lock std::defer_lock_t 不请求锁。 22 | 4. std::try_to_lock std::try_to_lock_t 尝试请求锁,但不阻塞线程,锁不可用时也会立即返回。std::adopt_lock std::adopt_lock_t 假定当前线程已经获得互斥对象的所有权,所以不再请求锁。 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 `lock_guard` 类摘要 ### 155 | 156 | template 157 | class lock_guard 158 | { // class with destructor that unlocks a mutex 159 | public: 160 | using mutex_type = _Mutex 161 | 162 | explicit lock_guard(_Mutex& _Mtx) 163 | : _MyMutex(_Mtx) 164 | { // construct and lock 165 | _MyMutex.lock(); 166 | } 167 | lock_guard(_Mutex& _Mtx, adopt_lock_t) 168 | : _MyMutex(_Mtx) 169 | { // construct but don't lock 170 | } 171 | ~lock_guard() noexcept/ unlock 172 | _MyMutex.unlock( 173 | { /); 174 | } 175 | lock_guard(const lock_guard&) = delete; 176 | lock_guard& operator=(const lock_guard&) = delete; 177 | private: 178 | _Mutex& _MyMutex; 179 | }; 180 | */ 181 | 182 | 183 | ### 4.1.6 `std::unique_lock` 类摘要 ### 184 | 185 | namespace std { 186 | template 187 | class unique_lock { 188 | public: 189 | typedef Mutex mutex_type; 190 | // 构造/拷贝/析构: 191 | unique_lock() noexcept; 192 | explicit unique_lock(mutex_type& m); 193 | unique_lock(mutex_type& m, defer_lock_t) noexcept; 194 | unique_lock(mutex_type& m, try_to_lock_t) noexcept; 195 | unique_lock(mutex_type& m, adopt_lock_t) noexcept; 196 | template 197 | unique_lock(mutex_type& m, const chrono::time_point& abs_time) noexcept; 198 | template 199 | unique_lock(mutex_type& m, const chrono::duration& rel_time) noexcept; 200 | ~unique_lock(); 201 | unique_lock(unique_lock const&) = delete; 202 | unique_lock& operator=(unique_lock const&) = delete; 203 | unique_lock(unique _lock&& u) noexcept; 204 | unique_lock& operator=(unique_lock&& u) noexcept; 205 | 206 | // 上锁操作: 207 | void lock(); 208 | bool try_lock(); 209 | template 210 | bool try_lock_for(const chrono::duration& rel_time); 211 | template 212 | bool try_lock_until(const chrono::time_point& abs_time); 213 | void unlock(); 214 | 215 | // 修改操作 216 | void swap(unique_lock& u) noexcept; 217 | mutex_type *release() noexcept; 218 | 219 | // observers: 220 | bool owns_lock() const noexcept; 221 | explicit operator bool () const noexcept; 222 | mutex_type* mutex() const noexcept; 223 | private: 224 | mutex_type *pm; // exposition only 225 | bool owns; // exposition only 226 | }; 227 | 228 | template 229 | void swap(unique_lock& x, unique_lock& y) noexcept; 230 | } 231 | -------------------------------------------------------------------------------- /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 | 经过多次运行,可以发现counter的值基本集中在3W多,4W多,5W多,而如果不用try_lock(),使用lock(),counter的值一定是100000。 49 | 50 | ### `std::recursive_mutex` 介绍 ### 51 | 52 | `std::recursive_mutex` 与 `std::mutex `一样,也是一种可以被上锁的对象,但是和 `std::mutex` 不同的是,`std::recursive_mutex` 允许同一个线程对互斥量多次上锁(即递归上锁),来获得对互斥量对象的多层所有权,`std::recursive_mutex` 释放互斥量时需要调用与该锁层次深度相同次数的 `unlock()`,可理解为 `lock()` 次数和 `unlock()` 次数相同,除此之外,`std::recursive_mutex` 的特性和 `std::mutex` 大致相同。 53 | 54 | #include // std::cout 55 | #include // std::thread 56 | #include // std::mutex 57 | 58 | class counter 59 | { 60 | public: 61 | counter() : count(0) { } 62 | 63 | int add(int val) { 64 | std::recursive_mutex::scoped_lock scoped_lock(mutex); 65 | count += val; 66 | return count; 67 | } 68 | int increment() { 69 | std::recursive_mutex::scoped_lock scoped_lock(mutex); 70 | return add(1); 71 | } 72 | 73 | private: 74 | boost::recursive_mutex mutex; 75 | int count; 76 | }; 77 | 78 | counter c; 79 | 80 | void change_count(void*) 81 | { 82 | std::cout << "count == " << c.increment() << std::endl; 83 | } 84 | 85 | int main(int, char*[]) 86 | { 87 | 88 | std::thread threads[10]; 89 | // spawn 10 threads: 90 | for (int i=0; i<10; ++i) 91 | threads[i] = std::thread(change_count, 0); 92 | 93 | for (auto& th : threads) th.join(); 94 | 95 | return 0; 96 | } 97 | 98 | ### std::time_mutex 介绍 ### 99 | 100 | `std::time_mutex` 比 `std::mutex` 多了两个成员函数,`try_lock_for()`,`try_lock_until()`。 101 | 102 | `try_lock_for` 函数接受一个时间范围,表示在这一段时间范围之内线程如果没有获得锁则被阻塞住(与 `std::mutex` 的 `try_lock()` 不同,try_lock 如果被调用时没有获得锁则直接返回 `false`),如果在此期间其他线程释放了锁,则该线程可以获得对互斥量的锁,如果超时(即在指定时间内还是没有获得锁),则返回 `false`。 103 | 104 | `try_lock_until` 函数则接受一个时间点作为参数,在指定时间点未到来之前线程如果没有获得锁则被阻塞住,如果在此期间其他线程释放了锁,则该线程可以获得对互斥量的锁,如果超时(即在指定时间内还是没有获得锁),则返回 `false`。 105 | 106 | 下面的小例子说明了 `std::time_mutex` 的用法([参考](http://www.cplusplus.com/reference/mutex/timed_mutex/try_lock_for/))。 107 | 108 | #include // std::cout 109 | #include // std::chrono::milliseconds 110 | #include // std::thread 111 | #include // std::timed_mutex 112 | 113 | std::timed_mutex mtx; 114 | 115 | void fireworks() { 116 | // waiting to get a lock: each thread prints "-" every 200ms: 117 | while (!mtx.try_lock_for(std::chrono::milliseconds(200))) { 118 | std::cout << "-"; 119 | } 120 | 121 | // got a lock! - wait for 1s, then this thread prints "*" 122 | std::this_thread::sleep_for(std::chrono::milliseconds(1000)); 123 | std::cout << "*\n"; 124 | mtx.unlock(); 125 | } 126 | 127 | int main () 128 | { 129 | std::thread threads[10]; 130 | // spawn 10 threads: 131 | for (int i=0; i<10; ++i) 132 | threads[i] = std::thread(fireworks); 133 | 134 | for (auto& th : threads) th.join(); 135 | 136 | return 0; 137 | } 138 | 139 | ### `std::recursive_timed_mutex` 介绍 ### 140 | 141 | 和 `std:recursive_mutex` 与 `std::mutex` 的关系一样,`std::recursive_timed_mutex` 的特性也可以从 `std::timed_mutex` 推导出来,感兴趣的同鞋可以自行查阅。;-) 142 | 143 | ### std::lock_guard 介绍 ### 144 | 145 | 与 Mutex RAII 相关,方便线程对互斥量上锁。例子([参考](http://www.cplusplus.com/reference/mutex/lock_guard/)): 146 | 147 | #include 148 | #include 149 | std::mutex mutex; 150 | void safe_thread() { 151 | try { 152 | std::lock_guard _guard(mutex); 153 | throw std::logic_error("logic error"); 154 | } catch (std::exception &ex) { 155 | std::cerr << "[caught] " << ex.what() << std::endl; 156 | } 157 | } 158 | int main() { 159 | safe_thread(); 160 | // 此处仍能上锁 161 | mutex.lock(); 162 | std::cout << "OK, still locked" << std::endl; 163 | mutex.unlock(); 164 | 165 | return 0; 166 | } 167 |
[caught] logic error
168 | OK, still locked 169 | 170 | ### `std::unique_lock` 介绍 ### 171 | 172 | 与 Mutex RAII 相关,方便线程对互斥量上锁,但提供了更好的上锁和解锁控制。例子([参考](http://www.cplusplus.com/reference/mutex/unique_lock/)): 173 | 174 | #include // std::cout 175 | #include // std::thread 176 | #include // std::mutex, std::unique_lock 177 | 178 | std::mutex mtx; // mutex for critical section 179 | 180 | void print_block (int n, char c) { 181 | // critical section (exclusive access to std::cout signaled by lifetime of lck): 182 | std::unique_lock lck (mtx); 183 | for (int i=0; i guard(coutMutex); 209 | std::cout << s.c_str() << std::endl; 210 | std::cout << "&s: " << &s << std::endl; 211 | std::cout << std::endl; 212 | } 213 | void main() { 214 | std::cout << std::endl; 215 | std::thread t1(addThreadLocal, "t1"); 216 | std::thread t2(addThreadLocal, "t2"); 217 | std::thread t3(addThreadLocal, "t3"); 218 | std::thread t4(addThreadLocal, "t4"); 219 | 220 | t1.join(); 221 | t2.join(); 222 | t3.join(); 223 | t4.join(); 224 | } 225 | 226 | hello from t1 227 | &s: 011D0368 每个线程s有其自己的副本 228 | hello from t2 229 | &s : 011D3598 230 | hello from t3 231 | &s : 011D4188 232 | hello from t4 233 | &s : 011D4D78 234 | 235 | 236 | 237 | -------------------------------------------------------------------------------- /zh/chapter4-Mutex/4.5 Mutex vs pthread.md: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /zh/chapter4-Mutex/README.md: -------------------------------------------------------------------------------- 1 | # 本章导读 # 2 | 3 | 本章主要内容如下: 4 | 5 | - `` 头文件中的类及相关函数的介绍,包括互斥量、各种锁类型的介绍,以及 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/wshilaji/Cplusplus-Concurrency-In-Action/blob/master/zh/chapter4-Mutex/4.1%20Mutex-header-synopsis.md) ## 12 | ### [4.1.1 `std::mutex` 类摘要](https://github.com/wshilaji/Cplusplus-Concurrency-In-Action/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/wshilaji/Cplusplus-Concurrency-In-Action/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/wshilaji/Cplusplus-Concurrency-In-Action/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/wshilaji/Cplusplus-Concurrency-In-Action/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/wshilaji/Cplusplus-Concurrency-In-Action/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/wshilaji/Cplusplus-Concurrency-In-Action/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/wshilaji/Cplusplus-Concurrency-In-Action/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/wshilaji/Cplusplus-Concurrency-In-Action/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/wshilaji/Cplusplus-Concurrency-In-Action/blob/master/zh/chapter4-Mutex/web-resources.md) ## 37 | -------------------------------------------------------------------------------- /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 | std::mutex mtx; // 全局互斥锁. 18 | std::condition_variable cv; // 全局条件变量. 19 | bool ready = false; // 全局标志位. 20 | void do_print_id(int id) 21 | { 22 | std::unique_lock lck(mtx); 23 | while (!ready) // 如果标志位不为 true, 则等待... 24 | cv.wait(lck); // 当前线程被阻塞, 当全局标志位变为 true 之后, 25 | // 线程被唤醒, 继续往下执行打印线程编号id. 26 | std::cout << "thread " << id << '\n'; 27 | } 28 | void go() 29 | { 30 | std::unique_lock lck(mtx); 31 | ready = true; // 设置全局标志位为 true. 32 | cv.notify_all(); // 唤醒所有线程. 33 | } 34 | int main() 35 | { 36 | std::thread threads[10]; 37 | // spawn 10 threads: 38 | for (int i = 0; i < 10; ++i) 39 | threads[i] = std::thread(do_print_id, i); 40 | 41 | std::cout << "10 threads ready to race...\n"; 42 | go(); // go! 43 | 44 | for (auto & th:threads) 45 | th.join(); 46 | 47 | return 0; 48 | } 49 | 50 | 执行结果如下: 51 | 52 | concurrency ) ./ConditionVariable-basic1 53 | 10 threads ready to race... 54 | thread 1 55 | thread 0 56 | thread 2 57 | thread 3 58 | thread 4 59 | thread 5 60 | thread 6 61 | thread 7 62 | thread 8 63 | thread 9 64 | 65 | 好了,对条件变量有了一个基本的了解之后,我们来看看 std::condition_variable 的各个成员函数。 66 | 67 | #### 5.2.1.1 `std::condition_variable` 构造函数 #### 68 | 69 | 70 | 71 | 72 |
default (1) condition_variable();
copy [deleted] (2) condition_variable (const condition_variable&) = delete;
73 | 74 | 75 | `std::condition_variable` 的拷贝构造函数被禁用,只提供了默认构造函数。 76 | 77 | #### 5.2.1.2 `std::condition_variable::wait()` 介绍 #### 78 | 79 | 80 | 81 | 82 | 83 | 85 |
unconditional (1) void wait (unique_lock<mutex>& lck);
predicate (2)
template <class Predicate>
 84 | void wait (unique_lock<mutex>& lck, Predicate pred); 
86 | 87 | `std::condition_variable` 提供了两种 `wait()` 函数。当前线程调用 `wait()` 后将被阻塞(此时当前线程应该获得了锁(`mutex`),不妨设获得锁 `lck`),直到另外某个线程调用 `notify_*` 唤醒了当前线程。 88 | 89 | 在线程被阻塞时,该函数会自动调用 `lck.unlock()` 释放锁,使得其他被阻塞在锁竞争上的线程得以继续执行。另外,一旦当前线程获得通知(`notified`,通常是另外某个线程调用 `notify_*` 唤醒了当前线程),`wait()` 函数也是自动调用 `lck.lock()`,使得 `lck` 的状态和 `wait` 函数被调用时相同。 90 | 91 | 在第二种情况下(即设置了 `Predicate`),只有当 `pred` 条件为 `false` 时调用 `wait()` 才会阻塞当前线程,并且在收到其他线程的通知后只有当 `pred` 为 `true` 时才会被解除阻塞。因此第二种情况类似以下代码: 92 | 93 | while (!pred()) wait(lck); 94 | 95 | 请看下面例子([参考](http://www.cplusplus.com/reference/condition_variable/condition_variable/wait/)): 96 | 97 | #include // std::cout 98 | #include // std::thread, std::this_thread::yield 99 | #include // std::mutex, std::unique_lock 100 | #include // std::condition_variable 101 | 102 | std::mutex mtx; 103 | std::condition_variable cv; 104 | int cargo = 0; 105 | 106 | bool shipment_available() //假 真 假 真... 107 | { 108 | return cargo != 0; 109 | } 110 | 111 | // 消费者线程. 112 | void consume(int n) 113 | { 114 | for (int i = 0; i < n; ++i) { 115 | std::unique_lock lck(mtx); 116 | cv.wait(lck, shipment_available); // while (!pred()) wait(lck); // 真 假 真 假 .. 117 | std::cout << cargo << '\n'; 118 | cargo = 0; 119 | } 120 | } 121 | int main() 122 | { 123 | std::thread consumer_thread(consume, 10); // 消费者线程. 124 | 125 | // 主线程为生产者线程, 生产 10 个物品. 126 | for (int i = 0; i < 10; ++i) { 127 | while (shipment_available()) //假 真 假 真 .... 128 | std::this_thread::yield(); 129 | std::unique_lock lck(mtx); 130 | cargo = i + 1; 131 | cv.notify_one(); 132 | } 133 | consumer_thread.join(); 134 | return 0; 135 | } 136 | 137 | 程序执行结果如下: 138 | 139 | concurrency ) ./ConditionVariable-wait 140 | 1 141 | 2 142 | 3 143 | 4 144 | 5 145 | 6 146 | 7 147 | 8 148 | 9 149 | 10 150 | 151 | #### 5.2.1.3 `std::condition_variable::wait_for()` 介绍 #### 152 | 153 | 154 | 155 | 161 | 162 | 163 | 168 | 169 |
unconditional (1) 156 |
template <class Rep, class Period>
157 | cv_status wait_for (unique_lock<mutex>& lck,
158 |                       const chrono::duration<Rep,Period>& rel_time);
159 | 
160 |
predicate (2) 164 |
template <class Rep, class Period, class Predicate>
165 | bool wait_for (unique_lock<mutex>& lck,
166 |                const chrono::duration<Rep, Period>& rel_time, Predicate pred);
167 |
170 | 171 | 与 `std::condition_variable::wait()` 类似,不过 `wait_for` 可以指定一个时间段,在当前线程收到通知或者指定的时间 `rel_time` 超时之前,该线程都会处于阻塞状态。而一旦超时或者收到了其他线程的通知,`wait_for` 返回,剩下的处理步骤和 `wait()` 类似。 172 | 173 | 另外,`wait_for` 的重载版本(`predicte(2)`)的最后一个参数 pred 表示 `wait_for` 的预测条件,只有当 `pred` 条件为 `false` 时调用 `wait()` 才会阻塞当前线程,并且在收到其他线程的通知后只有当 `pred` 为 `true` 时才会被解除阻塞,因此相当于如下代码: 174 | 175 | return wait_until (lck, chrono::steady_clock::now() + rel_time, std::move(pred)); 176 | 177 | 请看下面的例子([参考](http://www.cplusplus.com/reference/condition_variable/condition_variable/wait_for/)),下面的例子中,主线程等待 `th` 线程输入一个值,然后将 `th` 线程从终端接收的值打印出来,在 `th` 线程接受到值之前,主线程一直等待,每个一秒超时一次,并打印一个 "`.`": 178 | 179 | #include // std::cout 180 | #include // std::thread 181 | #include // std::chrono::seconds 182 | #include // std::mutex, std::unique_lock 183 | #include // std::condition_variable, std::cv_status 184 | 185 | std::condition_variable cv; 186 | 187 | int value; 188 | 189 | void do_read_value() 190 | { 191 | std::cin >> value; 192 | cv.notify_one(); 193 | } 194 | 195 | int main () 196 | { 197 | std::cout << "Please, enter an integer (I'll be printing dots): \n"; 198 | std::thread th(do_read_value); 199 | 200 | std::mutex mtx; 201 | std::unique_lock lck(mtx); 202 | while (cv.wait_for(lck,std::chrono::seconds(1)) == std::cv_status::timeout) { 203 | std::cout << '.'; 204 | std::cout.flush(); 205 | } 206 | 207 | std::cout << "You entered: " << value << '\n'; 208 | 209 | th.join(); 210 | return 0; 211 | } 212 | 213 | #### 5.2.1.4 `std::condition_variable::wait_until` 介绍 #### 214 | 215 | 216 | 217 | 223 | 224 | 225 | 231 | 232 |
unconditional (1) 218 |
template <class Clock, class Duration>
219 | cv_status wait_until (unique_lock<mutex>& lck,
220 |                         const chrono::time_point<Clock,Duration>& abs_time);
221 | 
222 |
predicate (2) 226 |
template <class Clock, class Duration, class Predicate>
227 | bool wait_until (unique_lock<mutex>& lck,
228 |                  const chrono::time_point<Clock,Duration>& abs_time,
229 |                  Predicate pred);
230 |
233 | 234 | 与 `std::condition_variable::wait_for` 类似,但是 `wait_until` 可以指定一个时间点,在当前线程收到通知或者指定的时间点 `abs_time` 超时之前,该线程都会处于阻塞状态。而一旦超时或者收到了其他线程的通知,`wait_until` 返回,剩下的处理步骤和 `wait_until()` 类似。 235 | 236 | 另外,`wait_until` 的重载版本(`predicte`(2))的最后一个参数 `pred` 表示 `wait_until` 的预测条件,只有当 `pred` 条件为 `false` 时调用 `wait()` 才会阻塞当前线程,并且在收到其他线程的通知后只有当 `pred` 为 `true` 时才会被解除阻塞,因此相当于如下代码: 237 | 238 | while (!pred()) 239 | if ( wait_until(lck,abs_time) == cv_status::timeout) 240 | return pred(); 241 | return true; 242 | 243 | ### `std::condition_variable::notify_one(`) 介绍 ### 244 | 245 | 唤醒某个等待(`wait`)线程。如果当前没有等待线程,则该函数什么也不做,如果同时存在多个等待线程,则唤醒某个线程是不确定的(`unspecified`)。 246 | 247 | 请看下例([参考](http://www.cplusplus.com/reference/condition_variable/condition_variable/notify_one/)): 248 | 249 | #include // std::cout 250 | #include // std::thread 251 | #include // std::mutex, std::unique_lock 252 | #include // std::condition_variable 253 | 254 | std::mutex mtx; 255 | std::condition_variable cv; 256 | 257 | int cargo = 0; // shared value by producers and consumers 258 | 259 | void consumer() 260 | { 261 | std::unique_lock < std::mutex > lck(mtx); 262 | while (cargo == 0) 263 | cv.wait(lck); 264 | std::cout << cargo << '\n'; 265 | cargo = 0; 266 | } 267 | 268 | void producer(int id) 269 | { 270 | std::unique_lock < std::mutex > lck(mtx); 271 | cargo = id; 272 | cv.notify_one(); 273 | } 274 | 275 | int main() 276 | { 277 | std::thread consumers[10], producers[10]; 278 | 279 | // spawn 10 consumers and 10 producers: 280 | for (int i = 0; i < 10; ++i) { 281 | consumers[i] = std::thread(consumer); 282 | producers[i] = std::thread(producer, i + 1); 283 | } 284 | 285 | // join them back: 286 | for (int i = 0; i < 10; ++i) { 287 | producers[i].join(); 288 | consumers[i].join(); 289 | } 290 | 291 | return 0; 292 | } 293 | 294 | #### 5.2.1.5 `std::condition_variable::notify_all()` 介绍 #### 295 | 296 | 唤醒所有的等待(`wait`)线程。如果当前没有等待线程,则该函数什么也不做。请看下面的例子: 297 | 298 | #include // std::cout 299 | #include // std::thread 300 | #include // std::mutex, std::unique_lock 301 | #include // std::condition_variable 302 | 303 | std::mutex mtx; // 全局互斥锁. 304 | std::condition_variable cv; // 全局条件变量. 305 | bool ready = false; // 全局标志位. 306 | 307 | void do_print_id(int id) 308 | { 309 | std::unique_lock lck(mtx); 310 | while (!ready) // 如果标志位不为 true, 则等待... 311 | cv.wait(lck); // 当前线程被阻塞, 当全局标志位变为 true 之后, 312 | // 线程被唤醒, 继续往下执行打印线程编号id. 313 | std::cout << "thread " << id << '\n'; 314 | } 315 | 316 | void go() 317 | { 318 | std::unique_lock lck(mtx); 319 | ready = true; // 设置全局标志位为 true. 320 | cv.notify_all(); // 唤醒所有线程. 321 | } 322 | 323 | int main() 324 | { 325 | std::thread threads[10]; 326 | // spawn 10 threads: 327 | for (int i = 0; i < 10; ++i) 328 | threads[i] = std::thread(do_print_id, i); 329 | 330 | std::cout << "10 threads ready to race...\n"; 331 | go(); // go! 332 | 333 | for (auto & th:threads) 334 | th.join(); 335 | 336 | return 0; 337 | } 338 | 339 | ### 5.2.2 `std::condition_variable_any` 介绍 ### 340 | 341 | 与 `std::condition_variable` 类似,只不过 `std::condition_variable_any` 的 `wait` 函数可以接受任何 `lockable` 参数,而 `std::condition_variable` 只能接受 `std::unique_lock` 类型的参数,除此以外,和 `std::condition_variable` 几乎完全一样。 342 | 343 | ### 5.2.3 `std::cv_status` 枚举类型介绍 ### 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 |
cv_status::no_timeoutwait_for 或者 wait_until 没有超时,即在规定的时间段内线程收到了通知。
cv_status::timeoutwait_for 或者 wait_until 超时。
355 | -------------------------------------------------------------------------------- /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/wshilaji/Cplusplus-Concurrency-In-Action/blob/master/zh/chapter5-Condition-Variable/5.1%20Condition-variable%20header%20synopsis.md) ## 12 | 13 | ## [5.2 条件变量详解](https://github.com/wshilaji/Cplusplus-Concurrency-In-Action/blob/master/zh/chapter5-Condition-Variable/5.2%20Condition-variable-tutorial.md) ## 14 | 15 | ## [5.3 辅助函数介绍](https://github.com/wshilaji/Cplusplus-Concurrency-In-Action/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/wshilaji/Cplusplus-Concurrency-In-Action/blob/master/zh/chapter5-Condition-Variable/web-resources.md) ## 22 | -------------------------------------------------------------------------------- /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/wshilaji/Cplusplus-Concurrency-In-Action/dc0ec9c42abc7b0fddf03e6289e7cde1f6ff3117/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/wshilaji/Cplusplus-Concurrency-In-Action/blob/master/zh/chapter6-Future/6.1%20Future-header-synopsis.md) ## 12 | 13 | ## 6.2 [异步任务提供者(Provider) 介绍](https://github.com/wshilaji/Cplusplus-Concurrency-In-Action/blob/master/zh/chapter6-Future/6.2%20Providers-tutorial.md) ## 14 | 15 | ## 6.3 [异步任务提供者(Provider) 介绍(续)](https://github.com/wshilaji/Cplusplus-Concurrency-In-Action/blob/master/zh/chapter6-Future/6.3%20Providers-tutorial-2.md) ## 16 | 17 | ## 6.4 [异步任务 Future 类型详解](https://github.com/wshilaji/Cplusplus-Concurrency-In-Action/blob/master/zh/chapter6-Future/6.4%20Future-tutorial.md) ## 18 | 19 | ## 6.5 [与异步任务相关的类型介绍](https://github.com/wshilaji/Cplusplus-Concurrency-In-Action/blob/master/zh/chapter6-Future/6.5%20Auxiliary-types.md) ## 20 | 21 | ## 6.6 [异步任务辅助函数 `std::async` 介绍](https://github.com/wshilaji/Cplusplus-Concurrency-In-Action/blob/master/zh/chapter6-Future/6.6%20Auxiliary-function.md) ## 22 | 23 | ## 6.7 [异步任务与多线程实例](https://github.com/wshilaji/Cplusplus-Concurrency-In-Action/blob/master/zh/chapter6-Future/6.7%20Future-multithreading-application.md) ## 24 | 25 | ## 6.8 [资料汇](https://github.com/wshilaji/Cplusplus-Concurrency-In-Action/blob/master/zh/chapter6-Future/web-resources.md) ## 26 | -------------------------------------------------------------------------------- /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/wshilaji/Cplusplus-Concurrency-In-Action/blob/master/zh/chapter7-Atomic/7.1%20Atomic-header-synopsis.md) ## 12 | 13 | ## 7.2 [`std::atomic_flag` 详解](https://github.com/wshilaji/Cplusplus-Concurrency-In-Action/blob/master/zh/chapter7-Atomic/7.2%20Atomic-flag-tutorial.md) ## 14 | 15 | ## 7.3 [基本 `std::atomic` 类型详解](https://github.com/wshilaji/Cplusplus-Concurrency-In-Action/blob/master/zh/chapter7-Atomic/7.3%20Atomic-tutorial.md) ## 16 | 17 | ## 7.4 [特化的 `std::atomic` 类型详解](https://github.com/wshilaji/Cplusplus-Concurrency-In-Action/blob/master/zh/chapter7-Atomic/7.4%20Atomic-tutorial2.md) ## 18 | 19 | ## 7.5 [C 风格的原子操作](https://github.com/wshilaji/Cplusplus-Concurrency-In-Action/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/wshilaji/Cplusplus-Concurrency-In-Action/blob/master/zh/chapter7-Atomic/web-resources.md) ## 26 | -------------------------------------------------------------------------------- /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/wshilaji/Cplusplus-Concurrency-In-Action/dc0ec9c42abc7b0fddf03e6289e7cde1f6ff3117/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/wshilaji/Cplusplus-Concurrency-In-Action/dc0ec9c42abc7b0fddf03e6289e7cde1f6ff3117/zh/chapter8-Memory-Model/8.3 Synchronization-and-ordering.md -------------------------------------------------------------------------------- /zh/chapter8-Memory-Model/Memory-ordering-in-C++11.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wshilaji/Cplusplus-Concurrency-In-Action/dc0ec9c42abc7b0fddf03e6289e7cde1f6ff3117/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/wshilaji/Cplusplus-Concurrency-In-Action/blob/master/zh/chapter8-Memory-Model/web-resources.md) ## 17 | -------------------------------------------------------------------------------- /zh/chapter8-Memory-Model/lock-free.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /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/wshilaji/Cplusplus-Concurrency-In-Action/dc0ec9c42abc7b0fddf03e6289e7cde1f6ff3117/zh/chapter9-Advanced-Thread-Management/README.md -------------------------------------------------------------------------------- /zh/chapter9-Advanced-Thread-Management/web-resources.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wshilaji/Cplusplus-Concurrency-In-Action/dc0ec9c42abc7b0fddf03e6289e7cde1f6ff3117/zh/chapter9-Advanced-Thread-Management/web-resources.md --------------------------------------------------------------------------------