深入理解 C++20 中的 `std::shared_ptr` 原子操作

embedded/2025/3/18 14:24:38/

文章目录

    • 1. `std::shared_ptr` 的线程安全问题
    • 2. `std::shared_ptr` 原子操作函数
      • 2.1 原子读取和存储
      • 2.2 原子交换
      • 2.3 原子比较交换
    • 3. 注意事项
    • 4. 示例
    • 5. 总结

在多线程编程中,线程安全是一个至关重要的问题。C++11 引入了 std::shared_ptr,它通过引用计数机制提供了资源管理的便利性。然而,当多个线程需要共享和操作同一个 std::shared_ptr 对象时,线程安全问题仍然需要特别关注。幸运的是,C++ 标准库提供了针对 std::shared_ptr 的原子操作函数,这些函数可以帮助我们安全地在多线程环境中使用 std::shared_ptr

1. std::shared_ptr 的线程安全问题

std::shared_ptr 的控制块是线程安全的,这意味着不同的 std::shared_ptr 对象可以同时访问同一个控制块,而不会引发数据竞争。然而,当多个线程需要访问和修改同一个 std::shared_ptr 对象时,问题就出现了。例如,如果一个线程正在通过 resetoperator= 修改 std::shared_ptr 的指向,而另一个线程正在读取它的值,那么就可能发生数据竞争。

2. std::shared_ptr 原子操作函数

为了在多线程环境中安全地使用 std::shared_ptr,C++11 引入了一系列原子操作函数。这些函数允许我们以原子方式对 std::shared_ptr 进行读取、存储、交换和比较交换操作。

2.1 原子读取和存储

  • std::atomic_loadstd::atomic_store:这两个函数允许我们以原子方式读取和存储 std::shared_ptr 的值。它们的显式版本(std::atomic_load_explicitstd::atomic_store_explicit)还允许我们指定内存顺序。
std::shared_ptr<int> ptr = std::make_shared<int>(42);
std::shared_ptr<int> loaded_ptr = std::atomic_load(&ptr); // 原子读取
std::atomic_store(&ptr, std::make_shared<int>(100)); // 原子存储

2.2 原子交换

  • std::atomic_exchange:这个函数允许我们以原子方式交换 std::shared_ptr 的值。它会返回交换前的值。
std::shared_ptr<int> ptr = std::make_shared<int>(42);
std::shared_ptr<int> new_ptr = std::make_shared<int>(100);
std::shared_ptr<int> old_ptr = std::atomic_exchange(&ptr, new_ptr); // 原子交换

2.3 原子比较交换

  • std::atomic_compare_exchange_weakstd::atomic_compare_exchange_strong:这两个函数允许我们以原子方式进行比较交换操作。它们的显式版本(std::atomic_compare_exchange_weak_explicitstd::atomic_compare_exchange_strong_explicit)还允许我们指定成功和失败时的内存顺序。
std::shared_ptr<int> ptr = std::make_shared<int>(42);
std::shared_ptr<int> expected = ptr;
std::shared_ptr<int> desired = std::make_shared<int>(100);
if (std::atomic_compare_exchange_strong(&ptr, &expected, desired)) {// 交换成功
} else {// 交换失败
}

3. 注意事项

  • 互斥锁实现:这些原子操作函数通常使用互斥锁实现。这意味着它们可能比直接操作 std::shared_ptr 更慢,但在多线程环境中是安全的。
  • 全局哈希表:互斥锁存储在全局哈希表中,指针值用作键。这可能会导致性能问题,尤其是在高并发场景下。
  • 并发 TS:并发 TS 提供了原子智能指针类 atomic_shared_ptratomic_weak_ptr,以替代对这些函数的使用。这些类提供了更直观的语法和更好的性能。

4. 示例

以下是一个使用 std::shared_ptr 原子操作函数的简单示例:

#include <iostream>
#include <memory>
#include <thread>
#include <atomic>void worker(std::shared_ptr<int> ptr) {std::shared_ptr<int> loaded_ptr = std::atomic_load(&ptr);std::cout << "Loaded value: " << *loaded_ptr << std::endl;
}int main() {std::shared_ptr<int> ptr = std::make_shared<int>(42);std::thread t1(worker, std::ref(ptr));std::thread t2(worker, std::ref(ptr));t1.join();t2.join();return 0;
}

在这个示例中,两个线程同时读取 std::shared_ptr 的值。通过使用 std::atomic_load,我们可以确保读取操作是原子的,从而避免数据竞争。

5. 总结

std::shared_ptr 的原子操作函数为我们提供了一种在多线程环境中安全使用 std::shared_ptr 的方法。虽然这些函数的实现可能涉及互斥锁,从而导致性能开销,但它们可以有效避免数据竞争。在高并发场景下,建议使用并发 TS 提供的原子智能指针类,以获得更好的性能。
std::atomic_… std::shared_ptr - cppreference.cn - C++参考手册

http://www.ppmy.cn/embedded/173616.html

相关文章

【VS小知识】VS如何保存UTF8

之前由于用C#开发服务器&#xff0c;无意间发现VS保存的.cs文件不是UTF8编码的&#xff0c;虽然文件内看着是中文&#xff0c;但服务器传递给客户端的数据不是中文而是乱码。今天就来讲一下如何将VS保存为UTF8。话不多说直接开始。 问题表现 我们拿Unity举例。我们在脚本文件中…

RAGFlow部署与使用(开源本地知识库管理系统,包括kibana配置)

一、RAGFlow 简介 戳我访问RAGFlow RAGFlow 是一款基于深度文档理解构建的开源 RAG&#xff08;Retrieval-Augmented Generation&#xff09;引擎。它可以给我们搭建本地知识库&#xff0c;将用户的知识文档上传到RAGFlow后&#xff0c;通过文档切分、向量入库&#xff0c;在…

本地部署Deep Seek-R1,搭建个人知识库——笔记

目录 一、本地部署 DeepSeek - R1 1&#xff1a;安装Ollama 2&#xff1a;部署DeepSeek - R1模型 3&#xff1a;安装Cherry Studio 二、构建私有知识库 一、本地部署 DeepSeek - R1 1&#xff1a;安装Ollama 1.打开Ollama下载安装 未科学上网&#xff0c;I 先打开迅雷再下…

css模拟雷达扫描动画

<div class"radar-scan"><div class"radar-container" /></div> 样式&#xff1a; .radar-scan {background-image: linear-gradient(0deg,transparent 24%,rgba(32, 255, 77, 0.15) 25%,rgba(32, 255, 77, 0.15) 26%,transparent 27%,…

为wordpress核心functions文件瘦身

在wordpress主题开发过程中&#xff0c;常会用到functions.php这个文件&#xff0c;通过这个文件&#xff0c;可以让wordpress主题变得很强大。但是随着&#xff0c;功能的越来越多functions文件也会变得越来越臃肿庞大。要修改起functions文件来&#xff0c;也就越麻烦。特别是…

Unity物理射线滤除某层

关键点&#xff1a;使用LayerMask&#xff0c;针对Physics里检测collider的射线&#xff08;raycast、OverlapSphere...&#xff09;都适用 1.使用layerMask过滤层 int ignoreLayer LayerMask.NameToLayer("IgnoreRaycast");// 获取要忽略的层 int layerMask ~(1…

Lisp语言的网络管理

Lisp语言在网络管理中的应用 引言 随着网络技术的迅猛发展和信息化进程的加速&#xff0c;网络管理的复杂性逐渐增加。网络管理员需要处理大量的数据&#xff0c;监控网络的各个方面&#xff0c;并进行故障排查。在众多编程语言中&#xff0c;Lisp以其独特的特性和灵活性&…

C# 分部类 详解

从C#2.0起支持分部类。 分部类&#xff1a;是一个类的多个部分&#xff0c;编译器可把它们合并成一个完整的类。 分部类的目的&#xff1a;将一个类的定义划分到多个文件中。通过分部类&#xff0c;由工具处理的文件可独立于开发者手动编码的文件。 1.1定义分部类 使用clas…