atomic包装自定义类型

news2024/11/17 4:27:22

在学习原子变量之初,学过很多它的操作,但是很多都是在内置类型(int、long等)上进行的学习和实验。这次由于工作需要,要使用 atomic 来包装自定义类型,因此打算好好探究一番,把它彻底搞懂。

当要使用 atomic 包装自定义类型的时候首先肯定会产生疑问——C++是否支持这样使用?

于是乎,去 cppreference 上搜了一下(C++17版本、其他的版本也支持),发现允许使用 atomic 包装平凡拷贝(TriviallyCopyable)的自定义类型。这时候又会有一个疑惑什么是平凡拷贝?如何确定自己定义的类型是不是平凡拷贝的?
在这里插入图片描述
从图中可以看出如果你不确定你自定义的结构是否是 TriviallyCopyable 的,你可以拿上面五个值打印一下,当全部为 true 时,那么恭喜你,你定义的结构体是 TriviallyCopyable 的,可以使用 atomic 来包装它。至于具体的平凡拷贝的定义可以去 cppreference 上查一下,此处不再展开(下文会简单提到如何看一个类型是不是 TriviallyCopyable 的)。

场景分析

目前程序中存在两种线程即: mertrics_thread (下文称为 M 线程,其作用是将全局变量中的值进行上报并在上报之后进行复位,数量为 1) 、worker_thread(下文称为 W 线程,其作用是采集某些工作函数运行耗时和次数并将其更新到全局变量中,数量为 3),还存在一个全局变量(用于记录采集到的值)。首先定义全局变量类型 Counters

struct Counters{
    uint64_t a;
    int b;
};

如果我们不对其加以修饰就直接实例化它,并使用起来,那么必然会造成数据竞争。因此我们需要用 atomic 包装它,即定义一个全局变量 counter_atomic

std::atomic<Counters> counter_atomic;

前文提到 atomic 包装的变量必须是 TriviallyCopyable 的,我们可以拿上面的五个 value 来看一下我们定义的结构是否满足

int main() {

    std::cout << std::is_trivially_copyable<Counters>::value << std::endl;
    std::cout << std::is_copy_constructible<Counters>::value << std::endl;
    std::cout << std::is_move_constructible<Counters>::value << std::endl;
    std::cout << std::is_copy_assignable<Counters>::value << std::endl;
    std::cout << std::is_move_assignable<Counters>::value << std::endl;

    return 0;
}

在这里插入图片描述

从输出可以发现,其是 TriviallyCopyable 的,那么如何保证我们定义的结构是 TriviallyCopyable 的呢?在我看来只要其内部的值全是内置类型,然后不要去自己捣鼓构造函数、拷贝和移动相关的函数就行了。(这里就是前面提到的下文啦!!!)

实现

前置知识

实现必然涉及到原子变量的 CAS 操作,exchange()compare_exchange_strong()compare_exchange_weak() 这几个函数。

首先来看 exchange, 其函数签名是

_Tp exchange(_Tp __d, memory_order __m = memory_order_seq_cst)

其使用场景是将原子变量包装的值更新为 __d, 并返回原子变量之前保存的值。这个函数正好满足 M 线程中需要进行的 “读取——复位” 的操作。

然后来看两个 “比较——交换” 函数,它们的函数签名是

bool compare_exchange_strong(_Tp& __e, _Tp __d, memory_order __m = memory_order_seq_cst)

bool compare_exchange_weak(_Tp& __e, _Tp __d, memory_order __m = memory_order_seq_cst)

其使用场景是:有一个期望值 __e,将其与原子变量中存储的值进行比较,当相等时,就把 __d 存储到原子变量中,并返回 true;当不相等时,就把原子变量中存储的值赋给 __e,并返回 false

_strong_weak 的区别在于后者可能在某些平台上存在 Spurious Failure 问题,即 _weak 版本可能会在 __e 的值和原子变量中存储的值相等时,返回 false(仅在某些平台上会这样,X86 上面不会这样子)。_strong 在遇到 Spurious Failure 问题时会进行重试,即其在内部存在一个循环来弥补 _weak 的不足。本质原因还是在某些平台上面使用一系列指令来实现 CAS 原语,而在 x86 这样的机器上面,直接使用一条指令来实现 CAS 原语。

#include <iostream>
#include <atomic>
#include <chrono>
#include <thread>


struct Counters{
    uint64_t a = 0;
    int b = 0;
};

std::atomic<Counters> counters_atomic;

void metrics_report(){

    while(true) {
        // 取出 counters_atomic 中的值,并进行复位
        Counters new_counters;
        Counters old_counters = counters_atomic.exchange(new_counters);

        // 模拟指标上报,将采集到的 old_counters 上报上去。
        // 每间隔 3s 上报一次采集到的信息
        std::cout << " a: " << old_counters.a << " b: " << old_counters.b << std::endl;
        std::this_thread::sleep_for(std::chrono::seconds(3));
    }
}

void worker(){

    while(true){
        uint64_t start_time = 1000000;

        // 模拟工作线程的耗时操作
        std::this_thread::sleep_for(std::chrono::milliseconds (500));

        uint64_t end_time = 3000000;
        uint64_t diff_time = end_time - start_time;

        Counters old_val = counters_atomic.load();
        Counters new_val;

        do{
            new_val = old_val;
            new_val.a += diff_time;
            new_val.b += 2;
        }while(!counters_atomic.compare_exchange_strong(old_val, new_val));

    }
}

int main() {

    std::thread worker_a(worker);
    std::thread worker_b(worker);
    std::thread worker_c(worker);

    std::thread metrics_thread(metrics_report);

    worker_a.join();
    worker_b.join();
    worker_c.join();
    metrics_thread.join();

    return 0;
}

参考

  1. std::atomic | compare_exchange_weak vs. compare_exchange_strong [duplicate]
  2. cppreference.com
  3. Understand std::atomic::compare_exchange_weak() in C++11

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

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

相关文章

iOS系统故障怎么办?这三种苹果手机系统修复方法你一定要知道

随着苹果手机使用时间越长&#xff0c;苹果手机有时也会出现系统问题&#xff0c;如卡顿、崩溃、无法启动等。这些问题不仅影响用户的使用体验&#xff0c;还可能导致数据丢失。因此&#xff0c;掌握苹果手机系统修复方法显得尤为重要。本文将详细介绍苹果手机系统修复的常见方…

冯喜运:5.24今日晚间黄金原油行情如何操作

【黄金消息面分析】&#xff1a;在经历了连续两个交易日的大幅下跌后&#xff0c;黄金市场在北京时间5月24日欧市早盘迎来了反弹。金价目前位于2338美元/盎司附近&#xff0c;市场对此轮波动表现出浓厚的兴趣。本文将深入分析黄金市场的最新动态&#xff0c;探讨其背后的逻辑&a…

非阻塞sokcet和epoll

在Muduo网络库中同时使用了非阻塞socket与epoll&#xff0c;在此简单梳理下。 非阻塞sokcet和epoll共同工作的过程主要涉及网络编程中的非阻塞I/O和事件驱动机制。下面将详细解释这两者如何协同工作&#xff1a; 非阻塞socket简介 在传统的阻塞socket编程中&#xff0c;当调用…

web前端项目已有阿里巴巴图标基础上,再次导入阿里巴巴图标的方法

如果是第一次导入阿里巴巴图标请参考: vue项目引入阿里云图标_vue引用阿里云图标fontclass-CSDN博客 本文主要想讲在项目原有阿里巴巴图标基础上,再次导入阿里巴巴图标的解决办法: 1.iconfont.json对应修改就行,这个简单一看就明白; 2.iconfont.js主要改动<symbol><…

3D模型旋转显示不全怎么办---模大狮模型网

在3D建模和渲染过程中&#xff0c;我们有时会遇到旋转模型时显示不全的问题。这种情况可能由多种原因造成&#xff0c;包括模型本身的问题、软件设置不当、硬件配置不足等。本文将为您详细介绍几种可能的解决方法&#xff0c;帮助您解决3D模型旋转显示不全的问题。 一、检查模型…

活动回顾 |观测云在杭州论坛上闪耀:教育创新与技术领导力的双重荣耀

第二届服务韧性工程论坛在杭州顺利闭幕&#xff0c;观测云以其在可观测性领域的杰出成就和创新成果&#xff0c;成为了论坛的瞩目焦点。在此次以“人工智能驱动运维研发革命&#xff0c;SRE 助力出海企业构建健壮的 IT 生态系统”为主题的盛会上&#xff0c;观测云积极参与了四…

物联网实战--平台篇之(十)“我的“页面设计

目录 一、页面布局 二、头像 三、修改密码 四、重新登录 本项目的交流QQ群:701889554 物联网实战--入门篇https://blog.csdn.net/ypp240124016/category_12609773.html 物联网实战--驱动篇https://blog.csdn.net/ypp240124016/category_12631333.html 本项目资源文件htt…

【论文阅读】 YOLOv10: Real-Time End-to-End Object Detection

文章目录 AbstractIntroductionRelated WorkMethodologyConsistent Dual Assignments for NMS-free Training &#xff08;无NMS训练的一致性双重任务分配&#xff09;Holistic Efficiency-Accuracy Driven Model Design &#xff08;效率-精度驱动的整体模型设计&#xff09; …

如何在window中快速建立多个文件夹?

时隔多年&#xff0c;再次开始撰写文章&#xff0c;但是这次却是以设计师的身份 1. 几个基础快捷键先记一下&#xff0c;要不更高级的玩儿不转&#xff08;1&#xff09;快速打开资源管理器&#xff08;2&#xff09;快速建立新文件夹&#xff08;3&#xff09;快速修改文件文件…

作为 App 开发者会推荐安装的 Mac App

Xcode&#xff0c;作为 App 开发者&#xff0c;必须安装的工具。当然&#xff0c;有经验的开发者不会从 Mac App Store 下载&#xff0c;而是从网站下载&#xff0c;除了安装过程更可控&#xff0c;也方便多版本共存。此外&#xff0c;我不信任任何第三方下载方式&#xff1a; …

[LLM-Agents]浅析Agent工具使用框架:MM-ReAct

上文LLM-Agents]详解Agent中工具使用Workflow提到MM-ReAct框架&#xff0c;通过结合ChatGPT 与视觉专家模型来解决复杂的视觉理解任务的框架。通过设计文本提示&#xff08;prompt design&#xff09;&#xff0c;使得语言模型能够接受、关联和处理多模态信息&#xff0c;如图像…

网络安全技术心得体会

网络与信息安全技术心得体会 通过对网络安全这门课程的学习&#xff0c;我进一步了解了网络安全技术的相关知识。大致来说&#xff0c;所谓网络安全指的是对网络系统中各类软硬件和数据信息等提供保护屏障&#xff0c;确保数据信息不受到恶意侵入、窃取等破坏&#xff0c;保证…

FaceFusion源码框架解读

FaceFusion源码框架解读 我的视频讲解&#xff1a;FaceFusion入门教学 FaceFusion官网 FaceFusion是一款开源的AI换脸工具&#xff0c;一款非常好用的换脸工具&#xff0c;操作简单&#xff0c;上手容易。 Facefusion&#xff1a;GitHub - facefusion/facefusion: Next gene…

最新Adaptive特征融合策略,涨点又高效,想发表论文可以参考

自适应特征融合是一种非常高效的数据处理方法&#xff0c;它比传统的特征更能适应不同的数据和任务需求&#xff0c;也因此拥有广泛的应用前景&#xff0c;是深度学习领域的研究热点。 这种方法通过动态选择和整合来自不同层次或尺度的特征信息&#xff0c;不仅显著提升了模型…

USB抓包工具:bushound安装及使用

一、环境搭建 下载busbound6.01安装包&#xff0c;安装完成&#xff0c;重启电脑。 二、工具配置 按照下图配置工具&#xff1a; 使能自动识别新设备 2. 设置抓取数据的容量 三、抓包 回到capture选项卡&#xff0c;在页面的右下角有个run的按钮&#xff0c;点击使能&…

nacos-opera(k8s)安装问题解决

整理一些关于k8s部署nacos出现的一些恶心的问题 网上说其他说的更改数据库连接都未解决。 在用nacos-opera想安装高可用nacos时连接mysql数据库报错: 报错具体项: No DataSource set 具体就是说没找到数据源。 第一个 检查一下nacos连接数据库配置 : 第二个 检查一下数据库…

视频监控汇聚平台LntonCVS通过GB/T28181国标协议实现视频监控平台的级联方案

近年来&#xff0c;随着网络视频监控应用范围的拓展&#xff0c;越来越多的政府部门和跨区域行业单位对视频监控的需求已经不局限于本地联网监控。他们正在探索在原有的本地联网监控基础上&#xff0c;建设省级乃至全国范围内的跨区域监控联网&#xff0c;以全面打造数据共享平…

【Unity2D:C#Script】实现角色射击功能

一、创建子弹预制体 1. 创建子弹预制体 2. 调整图片大小、层级 二、为子弹添加碰撞体积 1. 添加Box Collider 2D、Rigidbody 2D组件 2. 锁定z轴 三、编辑敌人脚本 注&#xff1a;在以下代码中&#xff0c;只显示本章节新增的代码&#xff0c;省略原有的代码 1. 为敌人添加生…

安科瑞为河南省促进分布式光伏发电健康持续发展提供解决方案

1 光伏、储能运维市场分析 在光伏、储能行业飞速发展的同时&#xff0c;已建的光伏、风力发电站和储能系统的监控、运维管理项目的招标也非常多&#xff0c;2023年上半年&#xff0c;光伏电站开发企业运维招标规模28.6GW&#xff0c;同比增长204.3%&#xff0c;上述28.6GW招标…

抖店一件代发,从0到1操作全流程

我是王路飞。 先说明一点&#xff0c;新手不需要纠结抖店一件代发&#xff08;即无货源模式&#xff09;还能不能做的问题。 无货源只是前期帮助新手阶段的你进入到这个市场里来的一种方式&#xff0c;不是你长期做店的思路。 入门之后&#xff0c;基本就转型为有货源去玩了…