当前位置: 首页 > news >正文

atomic原子对象的学习

在上一篇mutex的学习中,我们知道,在多线程环境下对一个变量同时进很读写,必须要加锁才能避免数据竞争。如果只是对一个简单类型的变量进行加锁,解锁,感觉是不是有点冗余,其实可以通过atomic类型达到同样的效果,那么是atomic原子类型呢?

原子类型的对象包含一个特定类型(T)的值。

原子对象的主要特点是,从不同线程访问该内部值时不会引发数据竞争(即,只要访问顺序正确,则这种行为是明确定义的)。通常情况下,对于所有其他对象,若在并发访问同一对象时可能引发数据竞争,则该操作被视为未定义行为。

此外,原子对象还能够通过指定不同的内存顺序,在其线程中同步访问其他非原子对象。

本篇记录atomic原子对象的学习。

以下是atomic常函数

成员函数

(构造函数)

构造原子对象
(公开成员函数)

operator=

存储值于原子对象
(公开成员函数)

is_lock_free

检查原子对象是否免锁
(公开成员函数)

store

原子地以非原子实参替换原子对象的值
(公开成员函数)

load

原子地获得原子对象的值
(公开成员函数)

operator T

从原子对象加载值
(公开成员函数)

exchange

原子地替换原子对象的值并获得它先前持有的值
(公开成员函数)

compare_exchange_weakcompare_exchange_strong

原子地比较原子对象与非原子实参的值,相等时进行原子交换,不相等时进行原子加载
(公开成员函数)

wait

(C++20)

阻塞线程直到被提醒且原子值更改
(公开成员函数)

notify_one

(C++20)

提醒至少一个在原子对象上的等待中阻塞的线程
(公开成员函数)

notify_all

(C++20)

提醒所有在原子对象上的等待中阻塞的线程
(公开成员函数)

常量

is_always_lock_free

[静态] (C++17)

指示该类型是否始终免锁
(公开静态成员常量)

特化成员函数

为整数、浮点数(C++20 起)和指针类型特化

fetch_add

原子地将实参加到存储于原子对象的值上,并返回先前保有的值
(公开成员函数)

fetch_sub

原子地从存储于原子对象的值减去实参,并获得先前保有的值
(公开成员函数)

operator+=operator-=

与原子值进行加、减
(公开成员函数)

仅为整数和指针类型特化

fetch_max

(C++26)

原子地进行实参和原子对象的值的 std::max,并获得先前保有的值
(公开成员函数)

fetch_min

(C++26)

原子地进行实参和原子对象的值的 std::min,并获得先前保有的值
(公开成员函数)

operator++operator++(int)operator--operator--(int)

令原子值增加或减少一
(公开成员函数)

仅为整数类型特化

fetch_and

原子地进行实参和原子对象的值的逐位与,并获得先前保有的值
(公开成员函数)

fetch_or

原子地进行实参和原子对象的值的逐位或,并获得先前保有的值
(公开成员函数)

fetch_xor

原子地进行实参和原子对象的值的逐位异或,并获得先前保有的值
(公开成员函数)

operator&=operator|=operator^=

与原子值进行逐位与、或、异或
(公开成员函数)

1.用atomic原子类型来代码mutex,atomic原子类型operator=赋值操作示例

//atomic_eaxmple1.cpp atomic example #include <iostream> // std::cout #include <thread> // std::thread #include <atomic> std::atomic<int> g_count = 0; //定义原子类型 //自增函数测试 void incrementation() { printf("incrementation========================>start lock\n"); for (int i = 0; i < 1000000; ++i) { g_count++; } //模拟耗时 std::this_thread::sleep_for(std::chrono::seconds(1)); //休眠1秒 printf("incrementation========================>end lock\n"); } int main() { std::thread th1(incrementation); std::thread th2(incrementation); //join线程 th1.join(); th2.join(); std::cout << "g_count======" << g_count << std::endl; //atomic::operator= g_count = 3000000; std::cout << "g_count======" << g_count << std::endl; //printf("g_count==================%d\n", g_count); return 0; }

编译运行:

以上结果可以看出使用atomic原子类型后,在多线程中达到了mutex的效果。同时atomic原子类型也支持=赋值操作。

2. is_lock_free()函数,检查当前原子变量在底层实现中,是否使用了“免锁(Lock-Free)”机制。

更直白地说,它就是问编译器:“对这个变量进行读写操作时,底层用的是 CPU 硬件的原子指令(快),还是偷偷用了全局互斥锁(慢,且可能导致线程阻塞)?

//atomic_eaxmple1.cpp atomic example #include <iostream> // std::cout #include <thread> // std::thread #include <atomic> std::atomic<int> g_count = 0; //定义原子类型 struct A { int a[100]; }; struct B { int x, y; }; //自增函数测试 void incrementation() { printf("incrementation========================>start lock\n"); for (int i = 0; i < 1000000; ++i) { g_count++; } //模拟耗时 std::this_thread::sleep_for(std::chrono::seconds(1)); //休眠1秒 printf("incrementation========================>end lock\n"); } int main() { std::thread th1(incrementation); std::thread th2(incrementation); //join线程 th1.join(); th2.join(); std::cout << "g_count======" << g_count << std::endl; //atomic::operator= g_count = 3000000; std::cout << "g_count======" << g_count << std::endl; printf("g_count 是否免锁?%s\n", g_count.is_lock_free() ? "true" : "false"); printf("std::atomic<A> 是否免锁?%s\n", std::atomic<A>{}.is_lock_free() ? "true" : "false"); printf("std::atomic<B> 是否免锁?%s\n", std::atomic<B>{}.is_lock_free() ? "true" : "false"); //printf("g_count==================%d\n", g_count); return 0; }

编译运行:

从结果验证,简单类型的数据是免锁的,操作快。复杂类型的操作慢,就是采用mutex原理。

3.store函数,以非原子实参替换原子对象的值

//atomic_eaxmple1.cpp atomic example #include <iostream> // std::cout #include <thread> // std::thread #include <atomic> std::atomic<int> g_count = 0; //定义原子类型 struct A { int a[100]; }; struct B { int x, y; }; //自增函数测试 void incrementation() { printf("incrementation========================>start lock\n"); for (int i = 0; i < 1000000; ++i) { g_count++; } //模拟耗时 std::this_thread::sleep_for(std::chrono::seconds(1)); //休眠1秒 printf("incrementation========================>end lock\n"); } int main() { std::thread th1(incrementation); std::thread th2(incrementation); //join线程 th1.join(); th2.join(); std::cout << "g_count======" << g_count << std::endl; //1. atomic::operator= g_count = 3000000; std::cout << "g_count======" << g_count << std::endl; //2. is_lock_free() printf("g_count 是否免锁?%s\n", g_count.is_lock_free() ? "true" : "false"); printf("std::atomic<A> 是否免锁?%s\n", std::atomic<A>{}.is_lock_free() ? "true" : "false"); printf("std::atomic<B> 是否免锁?%s\n", std::atomic<B>{}.is_lock_free() ? "true" : "false"); //3. atomic::store() g_count.store(3200000); std::cout << "g_count======" << g_count << std::endl; //printf("g_count==================%d\n", g_count); return 0; }

运行结果:

4.load()函数, 获得原子对象的值。上面示例可以看到用printf函数直接输出g_count是什么报错的,g_count是原子对象,并不是一个整型数据,cout可以打印是因为它解析了这种类型。

//atomic_eaxmple1.cpp atomic example #include <iostream> // std::cout #include <thread> // std::thread #include <atomic> std::atomic<int> g_count = 0; //定义原子类型 struct A { int a[100]; }; struct B { int x, y; }; //自增函数测试 void incrementation() { printf("incrementation========================>start lock\n"); for (int i = 0; i < 1000000; ++i) { g_count++; } //模拟耗时 std::this_thread::sleep_for(std::chrono::seconds(1)); //休眠1秒 printf("incrementation========================>end lock\n"); } int main() { std::thread th1(incrementation); std::thread th2(incrementation); //join线程 th1.join(); th2.join(); std::cout << "g_count======" << g_count << std::endl; //1. atomic::operator= g_count = 3000000; std::cout << "g_count======" << g_count << std::endl; //2. is_lock_free() printf("g_count 是否免锁?%s\n", g_count.is_lock_free() ? "true" : "false"); printf("std::atomic<A> 是否免锁?%s\n", std::atomic<A>{}.is_lock_free() ? "true" : "false"); printf("std::atomic<B> 是否免锁?%s\n", std::atomic<B>{}.is_lock_free() ? "true" : "false"); //3. atomic::store() g_count.store(3200000); std::cout << "g_count======" << g_count << std::endl; //4. atomic::load() printf("g_count==================%d\n", g_count.load()); return 0; }

编译运行:

5. opration T 加载并返回原子变量的当前值。等价于load()

//atomic_eaxmple2.cpp atomic::operator=/operator T example: #include <iostream> // std::cout #include <thread> // std::thread #include <atomic> std::atomic<int> foo = 0; std::atomic<int> bar = 0; std::atomic<float> fat = 2.3; void set_foo(int x) { foo = x; printf("foo===========%d\n", foo.load()); } void copy_foo_to_bar() { while (foo == 0) std::this_thread::yield();//让出CPU,由“运行”态转为“就绪”态 // bar = foo; 不能直接这样赋值 bar = static_cast<int>(foo); //取foo的值赋给bar } void copy_fat_to_foo() { // foo = fat; 不能直接这样赋值 //模拟耗时 std::this_thread::sleep_for(std::chrono::seconds(1)); //休眠1秒 foo = static_cast<int>(fat); //取fat的值赋给bar } void print_bar() { while (bar == 0) std::this_thread::yield();//让出CPU,由“运行”态转为“就绪”态 printf("bar===========%d\n", bar.load()); } int main() { std::thread first(print_bar); std::thread second(set_foo, 10); std::thread third(copy_foo_to_bar); std::thread fourth(copy_fat_to_foo); first.join(); second.join(); third.join(); fourth.join(); printf("foo===========%d\n", foo.load()); return 0; }

运行结果:

6. exchange函数 替换底层值

//atomic_eaxmple3.cpp atomic::exchange T example: //#include <iostream> // std::cout #include <thread> // std::thread #include <atomic> #include <vector> std::atomic<int> foo = 5; std::atomic<int> bar = 6; std::atomic<bool> ready(false); std::atomic<bool> winner(false); void count1m(int id) { while (!ready) {} // wait for the ready signal for (int i = 0; i < 1000000; ++i) {} // go!, count to 1 million if (!winner.exchange(true)) { printf("thread #%d won!\n", id); } }; int main() { printf("foo===========%d\n", foo.load());//5 printf("bar===========%d\n", bar.load()); foo.exchange(bar); //等同于store(bar) printf("foo===========%d\n", foo.load());//6 printf("bar===========%d\n", bar.load()); foo.exchange(9); printf("foo===========%d\n", foo.load());//9 printf("bar===========%d\n", bar.load()); std::vector<std::thread> threads; printf("spawning 10 threads that count to 1 million...\n"); for (int i = 1; i <= 10; ++i) threads.push_back(std::thread(count1m, i)); ready = true; for (auto& th : threads) th.join(); return 0; }

编译运行:

7. fetch_add函数,将实参加到存储于原子对象的值上,并返回先前保有的值

//atomic_eaxmple4.cpp atomic::fetch_add example: //#include <iostream> // std::cout #include <thread> // std::thread #include <atomic> #include <array> std::atomic<int> foo{ 5 }; std::atomic<long long> data{ 10 }; std::array<long long, 5> return_values{}; void do_work(int thread_num) { long long val = data.fetch_add(1, std::memory_order_relaxed); return_values[thread_num] = val; } int main() { printf("foo===========%d\n", foo.load());//5 int ret = foo.fetch_add(2); printf("foo===========%d\n", foo.load());//7 std::thread th0{ do_work, 0 }; std::thread th1{ do_work, 1 }; std::thread th2{ do_work, 2 }; std::thread th3{ do_work, 3 }; std::thread th4{ do_work, 4 }; th0.join(); th1.join(); th2.join(); th3.join(); th4.join(); printf("result = %lld\n", data.load()); for (long long val : return_values) printf("look return value: %lld\n", val); return 0; }

编译运行:

8.fetch_sub函数,从存储于原子对象的值减去实参,并获得先前保有的值

//atomic_eaxmple5.cpp atomic::fetch_sub example: //#include <iostream> // std::cout #include <thread> // std::thread #include <atomic> #include <array> std::atomic<int> foo{ 5 }; std::atomic<int> data{ 66 }; std::array<int, 5> return_values{}; void do_work(int thread_num) { int val = data.fetch_sub(2, std::memory_order_relaxed); return_values[thread_num] = val; } int main() { printf("foo===========%d\n", foo.load());//5 int ret = foo.fetch_sub(2); printf("foo===========%d\n", foo.load());//3 std::thread th0{ do_work, 0 }; std::thread th1{ do_work, 1 }; std::thread th2{ do_work, 2 }; std::thread th3{ do_work, 3 }; std::thread th4{ do_work, 4 }; th0.join(); th1.join(); th2.join(); th3.join(); th4.join(); printf("result = %d\n", data.load()); for (int val : return_values) printf("look return value: %d\n", val); return 0; }

编译运行

9.operator+=,operator-= 原子值进行加、减,operator++,operator--令原子值增加或减少一

//atomic_eaxmple6.cpp operator+=,operator-=,operator++,operator-- example: //#include <iostream> // std::cout #include <thread> // std::thread #include <atomic> #include <array> std::atomic<int> foo{ 5 }; std::atomic<int> bar{ 6 }; int main() { printf("foo===========%d\n", foo.load());//5 foo += 2;//加常数 printf("foo===========%d\n", foo.load());//7 foo -= 3; printf("foo===========%d\n", foo.load());//4 foo += bar; //加原子对象 printf("foo===========%d\n", foo.load());//10 foo--; printf("foo===========%d\n", foo.load());//9 bar++; printf("bar===========%d\n", bar.load());//7 return 0; }

编译运行:

10.fetch_and原子地进行实参和原子对象的值的逐位与,并获得先前保有的值
fetch_or原子地进行实参和原子对象的值的逐位或,并获得先前保有的值
fetch_xor 原子地进行实参和原子对象的值的逐位异或,并获得先前保有的值
operator&= operator|= operator^= 与原子值进行逐位与、或、异或

//atomic_eaxmple7.cpp fetch_and,fetch_or,fetch_xor ,operator&=, operator|=, operator^= example: //#include <iostream> // std::cout #include <thread> // std::thread #include <atomic> std::atomic<int> foo{ 5 }; int main() { printf("foo===========%d\n", foo.load());//5 int ret = foo. fetch_and (3); //1 printf("ret = %d, foo===========%d\n", ret, foo.load()); ret = foo.fetch_or(5);//5 printf("ret = %d, foo===========%d\n", ret, foo.load()); ret = foo.fetch_xor(2); //7 printf("ret = %d, foo===========%d\n", ret, foo.load()); foo &= 2;//2 printf("ret = %d, foo===========%d\n", ret, foo.load()); foo |= 3; //3 printf("ret = %d, foo===========%d\n", ret, foo.load()); foo ^= 5; //6 printf("ret = %d, foo===========%d\n", ret, foo.load()); return 0; }

编译运行:

http://www.cnnetsun.cn/news/4333100.html

相关文章:

  • 基于STM32的示波法电子血压计设计与实现全流程解析
  • Claude Code 安装与机械臂开发实战:从正逆解到 MuJoCo 仿真
  • 基于CNN的锂电池剩余寿命预测:MATLAB实现与工程实践
  • 你的问卷还在“凭感觉”出题?毕夏AI已经把问卷设计变成了一门“科学”
  • R语言混合效应模型全流程:从线性回归到GAM的进阶指南
  • NAATI翻译怎么办理?看完这篇不踩坑,3步搞定澳洲官方认可的翻译件!
  • 声纹识别项目实战:从GMM到x-vector全方案解析与调参经验
  • 保姆级论文AI使用教程✅零成本搞定整篇本科论文
  • 如何用AI高效写专著?精选AI专著生成工具,3天完成20万字!
  • 地震频谱分析实战:基于MATLAB的FFT实现与避坑指南
  • Simulink环境下BLDC六步换相与双闭环调速仿真建模全解析
  • Delphi工业上位机开发:dOPC Client Toolkit构建OPC客户端实践
  • 泰坦尼克号数据科学实战:从零入门特征工程与逻辑回归
  • AI文本水印为何容易被移除?从原理到检测失效的工程解析
  • 2020全国村名点shp数据从解压到应用全流程指南
  • 基于PyTorch与CNN的遥感图像滑坡识别:从数据到部署全流程解析
  • MATLAB实现GMR-1咬尾卷积码:从原理到工程仿真的完整指南
  • 2024电赛C题无线传输信号模拟系统:从方案选型到高分调试全解析
  • Agent验证技能开发实战:从创建到维护的完整指南
  • GAN生成虚拟人脸:从原理到训练调优的完整指南
  • Python接口自动化测试实战:从零搭建pytest框架
  • 端到端图神经网络社交关系推荐系统系统|PyTorch+ResNet+OpenCV完整源码+训练与部署教程
  • Spring Boot相册管理系统实战:从环境搭建到文件上传与分页
  • 四年级零基础孩子学C++,多久能考GESP六级
  • 检索增强生成全链路解析:从文档加载到评估的大模型知识库工程实践
  • PyTorch手写数字识别项目实战:从数据加载到模型部署的完整指南
  • 搜狐畅游校招Java笔试题解析:游戏开发工程师考点与实战
  • Java面试短期突击:从八股文到场景题的最小复习闭环
  • 基于Scrapy的Python爬虫架构设计与反爬应对策略
  • 呼叫中心IVR智能语音导航架构:自动分流、业务分层与通话提效技术解析