@@ -478,11 +478,41 @@ int main(){
478
478
479
479
2. 任一线程使用 shared_ptr 的**非 const** 成员函数
480
480
481
- ---
481
+ 使用 `std::atomic<shared_ptr>` 修改:
482
+
483
+ ```cpp
484
+ std::atomic<std::shared_ptr<Data>> data = std::make_shared<Data>();
485
+
486
+ void writer() {
487
+ for (int i = 0; i < 10; ++i) {
488
+ std::shared_ptr<Data> new_data = std::make_shared<Data>(i);
489
+ data.store(new_data); // 原子地替换所保有的值
490
+ std::this_thread::sleep_for(10ms);
491
+ }
492
+ }
493
+
494
+ void reader() {
495
+ for (int i = 0; i < 10; ++i) {
496
+ if (data.load()) {
497
+ std::cout << "读取线程值: " << data.load()->get_value() << std::endl;
498
+ }
499
+ else {
500
+ std::cout << "没有读取到数据" << std::endl;
501
+ }
502
+ std::this_thread::sleep_for(10ms);
503
+ }
504
+ }
505
+ ```
506
+
507
+ 很显然,这是线程安全的,` store ` 是原子操作,而 ` data.load()->get_value() ` 只是个读取操作。
482
508
483
- 不过事实上 ` std::atomic<std:: shared_ptr>` 的功能相当有限,单看它提供的修改接口(`=`、`store`、`load`、`exchang`)就能明白。如果要操作其保护的共享指针指向的资源还是得 `load()` 获取底层共享指针的副本。
509
+ 我知道,你肯定会想着: * 能不能调用 ` load() ` 成员函数原子地返回底层的 ` std::shared_ptr ` 再调用 ` swap ` 成员函数? *
484
510
511
+ 可以,但是** 没有意义** ,因为 ` load() ` 成员函数返回的是底层 ` std::shared_ptr ` 的** 副本** ,也就是一个临时对象。对这个临时对象调用 ` swap ` 并不会改变 ` data ` 本身的值,因此这种操作没有实际意义,尽管这不会引发数据竞争(因为是副本)。
485
512
513
+ 由于我们没有对读写操作进行同步,只是确保了操作的线程安全,所以多次运行时可能会看到一些无序的打印,这是正常的。
514
+
515
+ 不过事实上 ` std::atomic<std::shared_ptr> ` 的功能相当有限,单看它提供的修改接口(` = ` 、` store ` 、` load ` 、` exchang ` )就能明白。如果要操作其保护的共享指针指向的资源还是得 ` load() ` 获取底层共享指针的副本。此时再进行操作时就得考虑 ` std::shared_ptr ` 本身在多线程的支持了。
486
516
487
517
---
488
518
@@ -501,7 +531,7 @@ std::atomic<std::shared_ptr<int>> ptr = std::make_shared<int>(10);
501
531
2 . 解引用,等价 ` *get() ` ,返回了 ` int& `
502
532
3 . 直接修改这个引用所指向的资源。
503
533
504
- 在第一步时,已经脱离了 ` std::atomic ` 的保护,第二步就获取了被保护的数据的引用,第三步进行了修改,这导致了数据竞争。当然了,这是做法非常的愚蠢 ,只是为了表示,所谓的线程安全,也是要靠** 开发者的正确使用** 。
534
+ 在第一步时,已经脱离了 ` std::atomic ` 的保护,第二步就获取了被保护的数据的引用,第三步进行了修改,这导致了数据竞争。当然了,这种做法非常的愚蠢 ,只是为了表示,所谓的线程安全,也是要靠** 开发者的正确使用** 。
505
535
506
536
正确的用法如下:
507
537
@@ -512,6 +542,37 @@ ptr.store(std::make_shared<int>(100));
512
542
513
543
通过使用 ` store ` 成员函数,可以原子地替换 ` ptr ` 所保护的值。
514
544
545
+ ---
546
+
547
+ 最后再来稍微聊一聊提供的 ` wait ` 、` notify_one ` 、` notify_all ` 成员函数。这并非是 ` std::atomic<shared_ptr> ` 专属,C++20 以后任何 atomic 的特化都拥有这些成员函数,使用起来也都十分的简单,我们这里用一个简单的例子为你展示一下:
548
+
549
+ ``` cpp
550
+ std::atomic<std::shared_ptr<int >> ptr = std::make_shared<int >();
551
+
552
+ void wait_for_wake_up (){
553
+ std::osyncstream{ std::cout }
554
+ << "线程 "
555
+ << std::this_thread::get_id()
556
+ << " 阻塞,等待更新唤醒\n";
557
+
558
+ // 等待 ptr 变为其它值
559
+ ptr.wait(ptr.load());
560
+
561
+ std::osyncstream{ std::cout }
562
+ << "线程 "
563
+ << std::this_thread::get_id()
564
+ << " 已被唤醒\n";
565
+ }
566
+
567
+ void wake_up (){
568
+ std::this_thread::sleep_for (5s);
569
+
570
+ // 更新值并唤醒
571
+ ptr.store(std::make_shared<int>(10));
572
+ ptr.notify_one();
573
+ }
574
+ ```
575
+
515
576
## 内存次序
516
577
517
578
### 前言
0 commit comments