• C++11中原子变量std::atomic使用小记


    C++11中引入了原子变量模板类型,原子变量的引入,极大的方便了开发者开发多并发程序。工作中也经常使用原子变量,今天针对具体类型std::atomic和自定义类型std::atomic使用做个小小的总结。

    std::atomic原子变量的常用操作有:

    (1) -- ++操作

    (2)store(写操作)

    (3)load(读操作)

    (4)exchange(交换数据)

    (5)compare_exchange_weak (CAS)

    (6)compare_exchange_strong (加强版的CAS,性能略差)

    说明:

             compare_exchange_weak和compare_exchange_strong都是比较并更新操作,功能类似,compare_exchange_weak允许偶然出乎意料的返回false,在原始值和比较值一致时,通常比compare_exchange_strong有更高的性能。函数会传入比较值和更新值,进行如下操作:

    1)如果原始值与比较值一致,则将原始值修改为更新值,并返回true

    2)如果原始值与比较值不一致,则将比较值修改为原始值,并返回false

    1.简单累加操作

    在多线程中通常需要统计某个操作调用了多少次,此时可以利用原子变量统计比加锁更加高效和快捷,举例如下:

    1. #include
    2. #include
    3. static std::atomic<unsigned long long> s_sequence_index(0);
    4. void no_lock_statistics() {
    5. std::thread th1([&]{
    6. for (int i = 0; i < 1000000; i++) {
    7. ++s_sequence_index;
    8. }
    9. });
    10. std::thread th2([&]{
    11. for (int i = 0; i < 1000000; i++) {
    12. ++s_sequence_index;
    13. }
    14. });
    15. std::thread th3([&]{
    16. for (int i = 0; i < 1000000; i++) {
    17. ++s_sequence_index;
    18. }
    19. });
    20. std::thread th4([&]{
    21. for (int i = 0; i < 1000000; i++) {
    22. ++s_sequence_index;
    23. }
    24. });
    25. th1.join();
    26. th2.join();
    27. th3.join();
    28. th4.join();
    29. std::cout << "statistics:: " << s_sequence_index.load() << std::endl;
    30. }
    31. int main() {
    32. no_lock_statistics();
    33. return 0;
    34. }

    运行结果如下:

    通过运行看,4个线程同时对一个原子变量操作1000000次++,操作结束原子变量变为4000000,如果s_sequence_index改为普通的类型,操作结束结果不会变为4000000。

    2.获取并更新值操作

    在实际多线程开发中,往往需要比较,获取和更新某个变量,这一系列操作想要保证原子操作,如果没有原子变量,往往需要增加互斥锁。有了原子变量之后,操作就会变得很容易

    1. #include
    2. #include
    3. static std::atomic<unsigned long long> s_sequence_index(0);
    4. uint64_t get_index() {
    5. unsigned long long _index = 0;
    6. do {
    7. _index = s_sequence_index;
    8. } while(!s_sequence_index.compare_exchange_weak(_index, _index + 1));
    9. return (uint64_t)_index;
    10. }
    11. void test_get_index() {
    12. for (int i = 0; i < 100; i++) {
    13. std::thread([&]{
    14. std::cout << get_index() << std::endl;
    15. }).detach();
    16. }
    17. getchar();
    18. std::cout << "end:: " << s_sequence_index << std::endl;
    19. }
    20. int main() {
    21. test_get_index();
    22. return 0;
    23. }

    运行结果如下:

    如上图示,多线程不重复的获取原子变量的值,并对其进行更新,保证原子变量值的连续性和排他性。

     

    自定义类型原子变量std::atomic的常用操作有:

    (1)store(写操作)

    (2)load(读操作)

    (3)exchange(交换数据)

    (4)compare_exchange_weak (CAS)

    (5)compare_exchange_strong

    如下将展示使用原子操作多线程操作链表的例子

     

    1. #include
    2. #include
    3. template<typename T>
    4. class LockFreeList
    5. {
    6. private:
    7. struct Node
    8. {
    9. T _data;
    10. Node* _next;
    11. Node(T const& data): _data(data), _next(nullptr) {
    12. }
    13. };
    14. public:
    15. LockFreeList() : _head(nullptr) {
    16. }
    17. void push(T const& data) {
    18. Node* new_node = new Node(data);
    19. new_node->_next = _head.load();
    20. while (!_head.compare_exchange_weak(new_node->_next, new_node));
    21. }
    22. void show() {
    23. Node* tmpNode = _head;
    24. while(tmpNode) {
    25. std::cout << tmpNode->_data << " ";
    26. tmpNode = tmpNode->_next;
    27. }
    28. std::cout << std::endl;
    29. }
    30. private:
    31. std::atomic _head;
    32. };
    33. static LockFreeList<int> _s_lock_free_list;
    34. static std::atomic<int> _s_free_int(0);
    35. void test_lock_free_list() {
    36. for (int i = 0; i < 1000; i++) {
    37. std::thread([&](){
    38. _s_lock_free_list.push(_s_free_int++);
    39. }).detach();
    40. }
    41. getchar();
    42. _s_lock_free_list.show();
    43. }
    44. int main() {
    45. test_lock_free_list();
    46. return 0;
    47. }

    运行结果如下:

     通过测试以看到,数据被无重复的插入到了链表之中。

  • 相关阅读:
    【PX4】Ubuntu20.04+ROS Noetic 配置PX4-v1.12.2和Gazebo11联合仿真环境【教程】
    STC51单片机29——汇编语言 取表法 流水灯
    一种基于ChatGPT的高效吃瓜方式的探索和研究。
    OAuth2 的授权流程
    Sharding-JDBC实现分库分表
    【csdn第十期竞赛】dp专场
    为什么Redis默认序列化器处理之后的key会带有乱码?
    css
    基于ssm的高校学籍管理系统
    TCP中发送数据的情况
  • 原文地址:https://blog.csdn.net/hsy12342611/article/details/127764650