当前位置:首页 > 科技  > 软件

解锁 C++ 并发编程的钥匙:探索 Atomic 变量

来源: 责编: 时间:2023-12-04 17:25:33 350观看
导读最近在用c++搞项目,因为多线程要做一个类似cnt的保护,今天学习了c++的原子操作。探索c++的原子类型std::atomic 类型是 C++ 提供的一种机制,用于实现多线程之间的安全共享数据。它通过原子操作来确保对共享变量的操作是

最近在用c++搞项目,因为多线程要做一个类似cnt的保护,今天学习了c++的原子操作。wul28资讯网——每日最新资讯28at.com

wul28资讯网——每日最新资讯28at.com

探索c++的原子类型

std::atomic 类型是 C++ 提供的一种机制,用于实现多线程之间的安全共享数据。它通过原子操作来确保对共享变量的操作是不可分割的。在多线程环境下,如果没有适当的同步机制,对共享变量的读写可能会导致竞争条件,进而引发不确定的行为。std::atomic 类型提供了一种解决方案,让我们能够以线程安全的方式访问这些变量。wul28资讯网——每日最新资讯28at.com

关于具体的函数和详细介绍可以访问这里:https://cplusplus.com/reference/atomic/atomic/?kw=atomicwul28资讯网——每日最新资讯28at.com

wul28资讯网——每日最新资讯28at.com

这里介绍几个常用的:wul28资讯网——每日最新资讯28at.com

  • load 和 store:用于读取和写入原子变量的值。
  • exchange:交换原子变量的值,并返回之前的值。
  • compare_exchange_strong 和 compare_exchange_weak:比较并交换操作,可在特定条件下修改原子变量的值。
  • fetch_add 和 fetch_sub:原子地执行加法和减法操作,并返回之前的值。

这里原子操作后为什么要返回之前的值呢?wul28资讯网——每日最新资讯28at.com

以fetch_add为例,fetch_add是用于对原子变量进行原子性地增加操作。它执行一个原子的加法操作,并返回加法操作之前的原子变量的值。wul28资讯网——每日最新资讯28at.com

这种设计是基于并发编程中的常见需求。返回之前的值允许程序员在执行加法操作后,获取加法之前的原始值。这样做有以下几个方面的优点:wul28资讯网——每日最新资讯28at.com

  • 原子性操作的完整性:在多线程并发环境下,如果需要进行原子性的加法操作,同时又需要获取加法前的值,fetch_add 的设计能够保证这两个操作的原子性。它在单个原子操作中完成增加操作,并返回增加前的值,避免了在多线程环境下的竞态条件。
  • 避免竞态条件:返回之前的值可以让程序员在进行加法操作之后,检查原子变量的旧值,并根据旧值进行后续的操作。这对于实现一些特定的同步模式或算法是非常有用的,因为它避免了因为操作间的竞争导致的意外结果。

举个栗子

这里做一个简单的线程池,并实现一个task,task的任务就是对原子变量counter进行递增,最后我们看结果是否与预期一致,这里线程池实现10个线程,给线程池推送100000个task。wul28资讯网——每日最新资讯28at.com

#include <iostream>#include <thread>#include <mutex>#include <condition_variable>#include <queue>#include <functional>#include <atomic>class ThreadPool {public:    ThreadPool(size_t numThreads) : stop(false) {        for (size_t i = 0; i < numThreads; ++i) {            threads.emplace_back([this] {                while (true) {                    std::function<void()> task;                    {                        std::unique_lock<std::mutex> lock(queueMutex);                        condition.wait(lock, [this] { return stop || !tasks.empty(); });                        if (stop && tasks.empty()) {                            return;                        }                        task = std::move(tasks.front());                        tasks.pop();                    }                    task();                }            });        }    }    template <class F>    void AddTask(F&& f) {        {            std::lock_guard<std::mutex> lock(queueMutex);            tasks.emplace(std::forward<F>(f));        }        condition.notify_one();    }    ~ThreadPool() {        {            std::lock_guard<std::mutex> lock(queueMutex);            stop = true;        }        condition.notify_all();        for (std::thread& worker : threads) {            worker.join();        }    }private:    std::vector<std::thread> threads;    std::queue<std::function<void()>> tasks;    std::mutex queueMutex;    std::condition_variable condition;    bool stop;};int main() {    std::atomic<int> counter(0);    ThreadPool pool(10);    constexpr int numTasks = 100000;    for (int i = 0; i < numTasks; ++i) {        pool.AddTask([&counter]() {            counter++;        });    }    std::cout << "Waiting for tasks to complete..." << std::endl;    //注意:这里不会确保所有任务已经执行完毕,仅仅是等待一段时间以展示结果    std::this_thread::sleep_for(std::chrono::seconds(5));    std::cout << "Final Counter Value: " << counter << std::endl;    return 0;}

我们预期最后的结果是100000。g++编译,不要忘记加-lpthread,执行:wul28资讯网——每日最新资讯28at.com

wul28资讯网——每日最新资讯28at.com

细心的小伙伴可能发现我的代码直接使用的counter++,这里需要注意,这只是个简单的测试代码,实际项目中要最好使用counter.fetch_add(1),因为counter++不保证++是个原子操作。我在项目中遇到了该问题,最后加出来总会比预期值少,后来换成fetch_add后就正常了。wul28资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-37660-0.html解锁 C++ 并发编程的钥匙:探索 Atomic 变量

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: 一图详解五种前端架构

下一篇: 深入浅出JavaScript异步编程

标签:
  • 热门焦点
  • 卢伟冰长文解析K60至尊版 对Redmi有着里程碑式的意义

    在今天的Redmi后性能时代战略发布会结束之后,Redmi总经理卢伟冰又带来了一篇长文,详解了为什么 Redmi 要开启后性能时代?为什么选择和 MediaTek、Pixelworks 深度合作?以及后性
  • 对标苹果的灵动岛 华为带来实况窗功能

    继苹果的灵动岛之后,华为也在今天正式推出了“实况窗”功能。据今天鸿蒙OS 4.0的现场演示显示,华为的实况窗可以更高效的展现出实时通知,比如锁屏上就能看到外卖、打车、银行
  • 量化指标是与非:挽救被量化指标扼杀的技术团队

    作者 | 刘新翠整理 | 徐杰承本文整理自快狗打车技术总监刘新翠在WOT2023大会上的主题分享,更多精彩内容及现场PPT,请关注51CTO技术栈公众号,发消息【WOT2023PPT】即可直接领取
  • 如何通过Python线程池实现异步编程?

    线程池的概念和基本原理线程池是一种并发处理机制,它可以在程序启动时创建一组线程,并将它们置于等待任务的状态。当任务到达时,线程池中的某个线程会被唤醒并执行任务,执行完任
  • Python异步IO编程的进程/线程通信实现

    这篇文章再讲3种方式,同时讲4中进程间通信的方式一、 Python 中线程间通信的实现方式共享变量共享变量是多个线程可以共同访问的变量。在Python中,可以使用threading模块中的L
  • 一条抖音4亿人围观 ! 这家MCN比无忧传媒还野

    作者:Hiu 来源:互联网品牌官01 擦边少女空降热搜,幕后推手曝光被网友誉为&ldquo;纯欲天花板&rdquo;的女网红井川里予,近期因为一组哥特风照片登上热搜,引发了一场互联网世界关于
  • ESG的面子与里子

    来源 | 光子星球撰文 | 吴坤谚编辑 | 吴先之三伏大幕拉起,各地高温预警不绝,但处于厄尔尼诺大&ldquo;烤&rdquo;之下的除了众生,还有各大企业发布的ESG报告。ESG是&ldquo;环境保
  • iQOO Neo8 Pro评测:旗舰双芯加持 最强性能游戏旗舰

    【Techweb评测】去年10月,iQOO推出了一款Neo7手机,该机搭载了联发科天玑9000+,配备独显芯片Pro+,带来了同价位段最佳的游戏体验,一经上市便受到了诸多用
  • 2022爆款:ROG魔霸6 冰川散热系统持续护航

    喜逢开学季,各大商家开始推出自己的新产品,进行打折促销活动。对于忠实的端游爱好者来说,能够拥有一款梦寐以求的笔记本电脑是一件十分开心的事。但是现在的
Top