File tree 2 files changed +15
-4
lines changed
2 files changed +15
-4
lines changed Original file line number Diff line number Diff line change @@ -957,7 +957,7 @@ MSVC 无法使用 GCC 的编译器扩展,GCC 也肯定无法使用 MSVC 的扩
957
957
958
958
> ### 注意事项
959
959
>
960
- > 需要注意的是,在 MSVC 的实现中,`std::async` 策略为 [`launch::async`](https://zh.cppreference.com/w/cpp/thread/launch) 并不是每次都创建一个新的线程 ,而是从线程池获取线程。**这意味着无法保证线程局部变量在任务完成时会被销毁**。如果线程被回收并用于新的 `std::async` 调用,则旧的线程局部变量仍然存在。因此,**建议不要将线程局部变量与 `std::async` 一起使用**。[文档](https://learn.microsoft.com/zh-cn/cpp/standard-library/future-functions?view=msvc-170)。
960
+ > 需要注意的是,在 MSVC 的实现中,`std::async` 策略为 [`launch::async`](https://zh.cppreference.com/w/cpp/thread/launch) 却并不是每次都创建一个新的线程 ,而是从线程池获取线程。**这意味着无法保证线程局部变量在任务完成时会被销毁**。如果线程被回收并用于新的 `std::async` 调用,则旧的线程局部变量仍然存在。因此,**建议不要将线程局部变量与 `std::async` 一起使用**。[文档](https://learn.microsoft.com/zh-cn/cpp/standard-library/future-functions?view=msvc-170)。
961
961
>
962
962
> 虽然还没有讲 `std::async` ,不过还是可以注意一下这个问题,我们用一个简单的示例为你展示:
963
963
>
@@ -989,10 +989,20 @@ MSVC 无法使用 GCC 的编译器扩展,GCC 也肯定无法使用 MSVC 的扩
989
989
> 在不同编译器上的输出结果:
990
990
>
991
991
> - **Linux/Windows GCC、Clang**:会输出 `1`、`2`,因为线程变量 `x` 在每个任务中被正确销毁析构。
992
- > - **Windows MSVC**:会输出 `1`、`1`,因为线程被线程池复用,线程依然活跃,线程变量 x 也还未释放 。
992
+ > - **Windows MSVC**:会输出 `1`、`1`,因为线程被线程池复用,线程依然活跃,线程变量 x 也就还未释放 。
993
993
994
994
## CPU 变量
995
995
996
+ CPU 变量的概念很好理解。就像线程变量为每个线程提供独立的对象实例,互不干扰一样,CPU 变量也是如此。
997
+
998
+ 在创建 CPU 变量时,系统上的每个 CPU [^2] 都会获得该变量的一个副本。
999
+
1000
+ 实际上,这就是在说 Linux 2.6 [^3]引入的 **Per-CPU** 内核功能。
1001
+
1002
+ [^2]: “*每个 CPU*”,指的是系统中的每个物理处理器或每个逻辑处理器(如果超线程被启用)。
1003
+
1004
+ [^3]: [Linux 内核版本历史](https://zh.wikipedia.org/wiki/Linux%E5%86%85%E6%A0%B8%E7%89%88%E6%9C%AC%E5%8E%86%E5%8F%B2)。
1005
+
996
1006
## 总结
997
1007
998
1008
本章讨论了多线程的共享数据引发的恶性条件竞争会带来的问题。并说明了可以使用互斥量(`std::mutex`)保护共享数据,并且要注意互斥量上锁的“**粒度**”。C++标准库提供了很多工具,包括管理互斥量的管理类(`std::lock_guard`),但是互斥量只能解决它能解决的问题,并且它有自己的问题(**死锁**)。同时我们讲述了一些避免死锁的方法和技术。还讲了一下互斥量所有权转移。然后讨论了面对不同情况保护共享数据的不同方式,使用 `std::call_once()` 保护共享数据的初始化过程,使用读写锁(`std::shared_mutex`)保护不常更新的数据结构。以及特殊情况可能用到的互斥量 `recursive_mutex`,有些人可能喜欢称作:**递归锁**。最后聊了一下 `new`、`delete` 运算符的库函数实际是线程安全的,以及线程存储期。
Original file line number Diff line number Diff line change @@ -537,10 +537,11 @@ std::atomic<std::shared_ptr<int>> ptr = std::make_shared<int>(10);
537
537
538
538
``` cpp
539
539
std::atomic<std::shared_ptr<int >> ptr = std::make_shared<int >(10 );
540
- ptr.store(std::make_shared<int >(100 ));
540
+ std::atomic_ref<int > ref{ * ptr.load() };
541
+ ref = 100; // 原子地赋 100 给被引用的对象
541
542
```
542
543
543
- 通过使用 ` store ` 成员函数,可以原子地替换 ` ptr ` 所保护的值 。
544
+ 通过使用 [`std::atomic_ref`](https://zh.cppreference.com/w/cpp/atomic/atomic_ref) 我们得以确保在修改共享资源时保持操作的原子性,从而避免了数据竞争 。
544
545
545
546
---
546
547
You can’t perform that action at this time.
0 commit comments