File tree 3 files changed +9
-7
lines changed
3 files changed +9
-7
lines changed Original file line number Diff line number Diff line change @@ -692,7 +692,7 @@ void process_data(){
692
692
693
693
以上代码 ` std::once_flag ` 对象是全局命名空间作用域声明,如果你有需要,它也可以是类的成员。用于搭配 ` std::call_once ` 使用,保证线程安全的** 一次** 初始化。` std::call_once ` 只需要接受[ * 可调用 (Callable)* ] ( https://zh.cppreference.com/w/cpp/named_req/Callable ) 对象即可,也不要求一定是函数。
694
694
695
- > “** 初始化** ”,自然是 ** 一次** 。但是 ` std::call_once ` 也有一些例外情况(比如异常)会让传入的可调用对象被多次调用,即“** 多次** ”初始化:
695
+ > “** 初始化** ”,那自然是只有 ** 一次** 。但是 ` std::call_once ` 也有一些例外情况(比如异常)会让传入的可调用对象被多次调用,即“** 多次** ”初始化:
696
696
>
697
697
> ``` cpp
698
698
> std::once_flag flag;
@@ -720,6 +720,8 @@ void process_data(){
720
720
> ```
721
721
>
722
722
> [测试链接](https://godbolt.org/z/aWqfEchd6)。正常情况会保证传入的可调用对象只调用一次,即初始化只有一次。异常之类的是例外。
723
+ >
724
+ >这种行为很合理,因为异常代表操作失败,需要进行回溯和重置状态,符合语义和设计。
723
725
724
726
3. **静态局部变量初始化在 C++11 是线程安全**
725
727
@@ -735,7 +737,7 @@ void process_data(){
735
737
736
738
---
737
739
738
- 其实还有不少其他的做法或者反例,但是觉得没必要再聊了,因为本文不是详尽的文档 ,而是“** 教程** ”。
740
+ 其实还有不少其他的做法或者反例,但是觉得没必要再聊了,这些已经足够了,再多下去就冗余了。且本文不是详尽的文档 ,而是“** 教程** ”。
739
741
740
742
## 保护不常更新的数据结构
741
743
@@ -898,7 +900,7 @@ C++ 只保证了 `operator new`、`operator delete` 这两个方面的线程安
898
900
899
901
** 线程存储期** (也有人喜欢称作“[ * 线程局部存储* ] ( https://zh.wikipedia.org/wiki/%E7%BA%BF%E7%A8%8B%E5%B1%80%E9%83%A8%E5%AD%98%E5%82%A8 ) ”)的概念源自操作系统,是一种非常古老的机制,广泛应用于各种编程语言。线程存储期的对象在线程开始时分配,并在线程结束时释放。每个线程拥有自己独立的对象实例,互不干扰。在 C++11中,引入了[ ** ` thread_local ` ** ] ( https://zh.cppreference.com/w/cpp/keyword/thread_local ) 关键字,用于声明具有线程存储期的对象。
900
902
901
- 以下是一个示例代码 ,展示了 ` thread_local ` 关键字的使用:
903
+ 以下是一段示例代码 ,展示了 ` thread_local ` 关键字的使用:
902
904
903
905
``` cpp
904
906
int global_counter = 0 ;
Original file line number Diff line number Diff line change @@ -720,9 +720,9 @@ _Ty& get() {
720
720
}
721
721
```
722
722
723
- 如上所示,我们展示了 ` std::future ` 的所有特化 ` get ` 成员函数的实现。注意到了吗?尽管我们可能不了解移动构造函数的具体实现,但根据通用的语义,可以看出 ` future _Local{_STD move(*this)}; ` 将当前对象的共享状态转移给了这个局部对象,而局部对象在函数结束时析构。这意味着当前对象失去共享状态,并且状态被完全销毁。
723
+ 如上所示,我们展示了 ` std::future ` 的所有特化中 ` get ` 成员函数的实现。注意到了吗?尽管我们可能不了解移动构造函数的具体实现,但根据通用的语义,可以看出 ` future _Local{_STD move(*this)}; ` 将当前对象的共享状态转移给了这个局部对象,而局部对象在函数结束时析构。这意味着当前对象失去共享状态,并且状态被完全销毁。
724
724
725
- 另外一提,` std::future<T> ` 这个特化,它 ` return std::move ` 是为了支持只能移动的类型能够使用 ` get ` 返回值,参见前文的 ` move_only ` 类型。
725
+ 另外一提,` std::future<T> ` 这个特化,它 ` return std::move ` 是为了 ** 支持只能移动的类型 ** 能够使用 ` get ` 返回值,参见前文的 ` move_only ` 类型。
726
726
727
727
如果需要进行多次 ` get ` 调用,可以考虑使用下文提到的 ` std::shared_future ` 。
728
728
@@ -1229,7 +1229,7 @@ int main() {
1229
1229
1230
1230
---
1231
1231
1232
- 信号量常用于发信/提醒而非互斥 ,通过初始化该信号量为 0 从而阻塞尝试 acquire() 的接收者,直至提醒者通过调用 release(n) “发信”。在此方面可把信号量当作** 条件变量的替代品** ,** 通常它有更好的性能** 。
1232
+ 信号量常用于 * 发信/提醒 * 而非互斥 ,通过初始化该信号量为 0 从而阻塞尝试 acquire() 的接收者,直至提醒者通过调用 release(n) “发信”。在此方面可把信号量当作** 条件变量的替代品** ,** 通常它有更好的性能** 。
1233
1233
1234
1234
假设我们有一个 Web 服务器,它只能处理有限数量的并发请求。为了防止服务器过载,我们可以** 使用信号量来限制并发请求的数量** 。
1235
1235
Original file line number Diff line number Diff line change 8
8
9
9
## 学习注意事项
10
10
11
- &emsp ;&emsp ; 我们的教程中常包含许多外部链接,这并非当前描述不足或者不够严谨,而是为了考虑读者的水平和可能的扩展学习需求。同时,也希望者能让读者避免获取二手知识与理解 ,我们提供的链接基本都是较为专业的文档或官方网站。
11
+ &emsp ;&emsp ; 我们的教程中常包含许多外部链接,这并非当前描述不足或者不够严谨,而是为了考虑读者的水平和可能的扩展学习需求。同时,也希望能让读者避免获取二手知识与理解 ,我们提供的链接基本都是较为专业的文档或官方网站。
12
12
13
13
&emsp ;&emsp ; 虽然教程名为《现代 C++ 并发编程教程》,但我们也扩展涉及了许多其他知识,包括但不限于:Win32、POSIX API;MSVC STL、libstdc++、libc++ 对标准库的实现;GCC 与 MSVC 的编译器扩展,以及 Clang 对它们的兼容;使用 CMake + Qt 构建带 UI 的程序,展示多线程异步的必要性;不同架构的内存模型(例如 x86 架构内存模型:Total Store Order (TSO),较为严格的内存模型)。
14
14
You can’t perform that action at this time.
0 commit comments