C++ `unique_ptr` 多线程使用

news2025/7/16 9:22:49

C++ unique_ptr 多线程使用


一、核心结论
  • 操作同一个 unique_ptr:必须加锁(所有权转移是非原子操作)
  • 访问被管理对象:若对象非线程安全,仍需额外同步
  • 独立 unique_ptr 实例:不同线程操作不同实例时无需加锁

二、错误案例(未加锁导致数据竞争)
#include <iostream>
#include <memory>
#include <thread>

// 全局 unique_ptr(危险!)
std::unique_ptr<int> unsafe_ptr;

void unsafe_thread() {
    // 多线程同时修改同一个 unique_ptr
    unsafe_ptr = std::make_unique<int>(42);
}

int main() {
    std::thread t1(unsafe_thread);
    std::thread t2(unsafe_thread);
    
    t1.join();
    t2.join();
    
    if (unsafe_ptr) {
        std::cout << "危险操作结果: " << *unsafe_ptr << "\n";
    } else {
        std::cout << "指针已丢失\n"; // 可能输出
    }
    return 0;
}

可能输出(结果不确定):

指针已丢失
Segmentation fault (core dumped)  // 可能崩溃

三、正确实现(加锁保护所有权转移)
#include <iostream>
#include <memory>
#include <thread>
#include <mutex>

// 全局资源
std::unique_ptr<int> safe_ptr;
std::mutex ptr_mutex;

void safe_writer(int val) {
    std::lock_guard<std::mutex> lock(ptr_mutex);
    safe_ptr = std::make_unique<int>(val);  // 安全修改
}

void safe_reader() {
    std::unique_ptr<int> local_copy;
    {
        std::lock_guard<std::mutex> lock(ptr_mutex);
        if (safe_ptr) {
            local_copy = std::make_unique<int>(*safe_ptr);  // 安全拷贝值
        }
    }
    
    if (local_copy) {
        std::cout << "安全读取: " << *local_copy << "\n";
    }
}

int main() {
    std::thread writers[2];
    std::thread readers[2];
    
    // 启动写入线程
    for (int i = 0; i < 2; ++i) {
        writers[i] = std::thread(safe_writer, i * 100);
    }
    
    // 启动读取线程
    for (int i = 0; i < 2; ++i) {
        readers[i] = std::thread(safe_reader);
    }
    
    for (auto& t : writers) t.join();
    for (auto& t : readers) t.join();
    
    return 0;
}

典型输出(结果确定):

安全读取: 100
安全读取: 100

四、分步详解
步骤1:理解所有权独占性
  • unique_ptr 的内存布局:
    [对象指针] --> 被管理对象
    
  • 所有权转移(移动构造/赋值)会清空原指针
  • 两个线程同时修改同一指针会导致 数据竞争
步骤2:加锁保护所有权操作
std::lock_guard<std::mutex> lock(mtx);
global_ptr = std::move(new_ptr);  // 安全转移所有权
步骤3:安全访问被管理对象

若对象需要多线程访问:

class ThreadUnsafeObject {
    int counter = 0;
public:
    void increment() { ++counter; }  // 非线程安全
};

std::unique_ptr<ThreadUnsafeObject> obj_ptr;

void thread_work() {
    std::unique_ptr<ThreadUnsafeObject> local;
    {
        std::lock_guard<std::mutex> lock(mtx);
        local = std::move(obj_ptr);  // 独占所有权
    }
    
    // 现在可以安全使用 local(单线程独占)
    if (local) {
        local->increment();
    }
    
    // 交还所有权
    {
        std::lock_guard<std::mutex> lock(mtx);
        obj_ptr = std::move(local);
    }
}

五、编译与测试
  1. 编译命令

    g++ -std=c++17 -pthread -o unique_ptr_thread unique_ptr_thread.cpp
    
  2. 运行正确案例

    ./unique_ptr_thread
    安全读取: 100
    安全读取: 100
    

六、最佳实践总结
场景处理方案
多线程修改同一 unique_ptr必须使用 std::mutex 保护
多线程访问被管理对象通过转移所有权实现独占访问,或为对象本身添加同步机制
高频所有权转移使用 std::deque 缓冲任务,减少锁竞争
需要共享访问改用 std::shared_ptr(注意其不同的线程安全规则)

七、高级模式:所有权传递
#include <queue>
#include <mutex>

template<typename T>
class SafeQueue {
    std::queue<std::unique_ptr<T>> queue_;
    std::mutex mtx_;
public:
    void push(std::unique_ptr<T> ptr) {
        std::lock_guard<std::mutex> lock(mtx_);
        queue_.push(std::move(ptr));
    }
    
    std::unique_ptr<T> pop() {
        std::lock_guard<std::mutex> lock(mtx_);
        if (queue_.empty()) return nullptr;
        auto ptr = std::move(queue_.front());
        queue_.pop();
        return ptr;
    }
};

// 使用示例
SafeQueue<int> safe_queue;

void producer() {
    safe_queue.push(std::make_unique<int>(42));
}

void consumer() {
    auto ptr = safe_queue.pop();
    if (ptr) {
        std::cout << "消费: " << *ptr << "\n";
    }
}

通过合理应用这些模式,可以在多线程环境中安全高效地使用 unique_ptr,充分发挥其独占所有权的优势。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2338268.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Flink介绍——实时计算核心论文之Kafka论文详解

引入 我们通过S4和Storm论文的以下文章&#xff0c;已经对S4和Storm有了不错的认识&#xff1a; S4论文详解S4论文总结Storm论文详解Storm论文总结 不过&#xff0c;在讲解这两篇论文的时候&#xff0c;我们其实没有去搞清楚对应的流式数据是从哪里来的。虽然S4里有Keyless …

AI Agents系列之构建多智能体系统

&#x1f9e0; 向所有学习者致敬&#xff01; “学习不是装满一桶水&#xff0c;而是点燃一把火。” —— 叶芝 我的博客主页&#xff1a; https://lizheng.blog.csdn.net &#x1f310; 欢迎点击加入AI人工智能社区&#xff01; &#x1f680; 让我们一起努力&#xff0c;共创…

OJ笔试强训_1至24天

OJ笔试强训 Day01 [NOIP2010]数字统计_牛客题霸_牛客网 点击消除_牛客题霸_牛客网 两个数组的交集_牛客题霸_牛客网 Day02 牛牛的快递_牛客题霸_牛客网 最小花费爬楼梯_牛客题霸_牛客网 数组中两个字符串的最小距离__牛客网 Day03 简写单词_牛客题霸_牛客网 dd爱框框_…

3款顶流云电脑与传统电脑性能PK战:START云游戏/无影云/ToDesk云电脑谁更流畅?

这里写目录标题 一、前言二、本地机器配置环境三、START云游戏/无影云/ToDesk云电脑配置对比3.1 START云游戏3.2 无影云个人版3.3 ToDesk云电脑 四、本地电脑与云电脑性能实战4.1 游戏场景体验4.1.1 本地电脑测试4.1.2 云电脑测试英雄联盟黑神话悟空其他游戏 4.2 主流设计场景体…

java IO/NIO/AIO

(✪▽✪)曼波~~~~&#xff01;让曼波用最可爱的赛马娘方式给你讲解吧&#xff01;(⁄ ⁄•⁄ω⁄•⁄ ⁄) &#x1f3a0;曼波思维导图大冲刺&#xff08;先看框架再看细节哦&#xff09;&#xff1a; &#x1f4da; 解释 Java 中 IO、NIO、AIO 的区别和适用场景&#xff1a; …

java输出、输入语句

先创建一个用于测试的java 编写程序 #java.util使java标准库的一个包&#xff0c;这里拉取Scanner类 import java.util.Scanner;public class VariableTest {public static void main(String[] args) {#创建一个 Scanner 对象Scanner scanner new Scanner(System.in);System.…

宏基因组产品升级——抗菌肽数据库APD

抗菌肽&#xff08;Antimicrobial Peptides&#xff0c;简称AMPs&#xff09;是一类存在于多种生物体中的天然分子。它们在抵御微生物感染中扮演着重要角色&#xff0c;发挥着先天免疫反应的作用。抗菌肽功能分类广泛&#xff0c;包括&#xff1a;抗菌&#xff0c;抗生物膜&…

线程池七个参数的含义

Java中的线程池里七个参数的以及其各自的含义 面试题&#xff1a;说一下线程池七个参数的含义&#xff1f; 所谓的线程池的 7 大参数是指&#xff0c;在使用 ThreadPoolExecutor 创建线程池时所设置的 7 个参数&#xff0c;如以下源码所示&#xff1a; public ThreadPoolExe…

Windows suwellofd 阅读器-v5.0.25.0320

Windows suwellofd 阅读器 链接&#xff1a;https://pan.xunlei.com/s/VOO7tUkTHHTTjSe39CeVkUHbA1?pwd3ibx# OFD(Open Fixed-layout Document) &#xff0c; 数科OFD阅读器支持国标版式、可信阅读、是电子发票、电子证照&#xff0c;电子病历等电子文件理想阅读工具。 多格…

三大等待和三大切换

三大等待 1、三大等待&#xff1a;等待的方式有三种&#xff1a;强制等待&#xff0c;隐性等待&#xff0c;显性等待。 1、强制等待&#xff1a;time.sleep(2)&#xff0c;秒 优点&#xff1a;使用简单缺点&#xff1a;等待时间把握不准&#xff0c;容易造成时间浪费或者等待时…

告别定时任务!用Dagster监听器实现秒级数据响应自动化

在数据管道开发中&#xff0c;我们经常面临需要根据外部事件触发计算任务的场景。传统基于时间的调度方式存在资源浪费和时效性不足的问题。本文将通过Dagster的**传感器&#xff08;Sensor&#xff09;**功能&#xff0c;演示如何构建事件驱动的数据处理流程。 场景模拟&…

【Unity】打包TextMeshPro的字体

前言 在Unity中&#xff0c;TextMeshPro与常规 Text 组件相比提供了更高级的文本呈现功能&#xff0c;TextMesh Pro 可以处理各种语言&#xff0c;包括中文。我们可以轻松地在 Unity 项目中使用中文&#xff0c;而不必担心字体和布局问题。TextMeshPro需要的字体资源就需要我们…

51单片机实验五:A/D和D/A转换

一、实验环境与实验器材 环境&#xff1a;Keli&#xff0c;STC-ISP烧写软件,Proteus. 器材&#xff1a;TX-1C单片机&#xff08;STC89C52RC&#xff09;、电脑。 二、 实验内容及实验步骤 1.A/D转换 概念&#xff1a;模数转换是将连续的模拟信号转换为离散的数字信…

使用VHD虚拟磁盘安装双系统,避免磁盘分区

前言 很多时候&#xff0c;我们对现在的操作系统不满意,就想要自己安装一个双系统 但是安装双系统又涉及到硬盘分区,非常复杂,容易造成数据问题 虚拟机的话有经常用的不爽,这里其实有一个介于虚拟机和双系统之间的解决方法,就是使用虚拟硬盘文件安装系统. 相当于系统在机上…

Kafka消费者端重平衡流程

重平衡的完整流程需要消费者 端和协调者组件共同参与才能完成。我们先从消费者的视角来审视一下重平衡的流程。在消费者端&#xff0c;重平衡分为两个步骤&#xff1a;分别是加入组和等待领导者消费者&#xff08;Leader Consumer&#xff09;分配方案。这两个步骤分别对应两类…

Django之modelform使用

Django新增修改数据功能优化 目录 1.新增数据功能优化 2.修改数据功能优化 在我们做数据优化处理之前, 我们先回顾下传统的写法, 是如何实现增加修改的。 我们需要在templates里面新建前端的页面, 需要有新增还要删除, 比如说员工数据的新增, 那需要有很多个输入框, 那html…

云轴科技ZStack入选中国人工智能产业发展联盟《大模型应用交付供应商名录》

2025年4月8日至9日&#xff0c;中国人工智能产业发展联盟&#xff08;以下简称AIIA&#xff09;第十四次全体会议暨人工智能赋能新型工业化深度行&#xff08;南京站&#xff09;在南京召开。工业和信息化部科技司副司长杜广达&#xff0c;中国信息通信研究院院长、中国人工智能…

写论文时降AIGC和降重的一些注意事项

‘ 写一些研究成果&#xff0c;英文不是很好&#xff0c;用有道翻译过来句子很简单&#xff0c;句型很单一。那么你会考虑用ai吗&#xff1f; 如果语句太正式&#xff0c;高级&#xff0c;会被误判成aigc &#xff0c;慎重选择ai润色。 有的话就算没有用ai生成&#xff0c;但…

AI 编程工具—如何在 Cursor 中集成使用 MCP工具

AI 编程工具—如何在 Cursor 中集成使用 MCP工具 这里我们给出了常用的MCP 聚合工具,也就是我们可以在这些网站找MCP服务 这是一个MCP Server共享平台,用户可以在上面发布和下载MCP Server配置。在这里可以选择你需要的MCP 服务。 如果你不知道你的mcp 对应的server 名称也不…

《软件设计师》复习笔记(12.2)——成本管理、配置管理

目录 一、项目成本管理 1. 定义 2. 主要过程 3. 成本类型 4. 其他概念 真题示例&#xff1a; 二、软件配置管理 1. 定义 2. 主要活动 3. 配置项 4. 基线&#xff08;Baseline&#xff09; 5. 配置库类型 真题示例&#xff1a; 一、项目成本管理 1. 定义 在批准…