C++11并发编程:原子操作atomic

一:概述

  项目中经常用遇到多线程操作共享数据问题,常用的处理方式是对共享数据进行加锁,如果多线程操作共享变量也同样采用这种方式。

  为什么要对共享变量加锁或使用原子操作?如两个线程操作同一变量过程中,一个线程执行过程中可能被内核临时挂起,这就是线程切换,当内核再次切换到该线程时,之前的数据可能已被修改,不能保证原子操作。

  C++11提供了个原子的类和方法atomic,保证了多线程对变量原子性操作,相比加锁机制mutex.locak(),mutex.unlocak(),性能有几倍的提升。

  所需头文件<atomic>

二:错误代码

 1 //全局变量
 2 int g_num = 0;
 3
 4 void fun()
 5 {
 6     for (int i = 0; i < 10000000; i++)
 7     {
 8         g_num++;
 9     }
10     return ;
11 }
12
13 int main()
14 {
15     //创建线程1
16     thread t1(fun);
17     //创建线程2
18     thread t2(fun);
19     t1.join();
20     t2.join();
21
22     cout << g_num << endl;
23     getchar();
24     return 1;
25 }

应该输出结果20000000,实际每次结果都不一样,总是小于该值,正是由于多线程操作同一变量而没有保证原子性导致的。

三:加锁代码

 1 //全局变量
 2 int g_num = 0;
 3 mutex m_mutex;
 4
 5 void fun()
 6 {
 7     for (int i = 0; i < 10000000; i++)
 8     {
 9         m_mutex.lock();
10         g_num++;
11         m_mutex.unlock();
12     }
13     return ;
14 }
15
16 int main()
17 {
18     //获取当前毫秒时间戳
19     typedef chrono::time_point<chrono::system_clock, chrono::milliseconds> microClock_type;
20     microClock_type tp1 = chrono::time_point_cast<chrono::milliseconds>(chrono::system_clock::now());
21     long long time1 = tp1.time_since_epoch().count();
22
23     //创建线程
24     thread t1(fun);
25     thread t2(fun);
26     t1.join();
27     t2.join();
28
29     cout << "总数:" << g_num << endl;
30
31     //获取当前毫秒时间戳
32     microClock_type tp2 = chrono::time_point_cast<chrono::milliseconds>(chrono::system_clock::now());
33     long long time2 = tp2.time_since_epoch().count();
34     cout << "耗时:" << time2 - time1 << "ms" << endl;
35
36     getchar();
37     return 1;
38 }

执行结果:多次测试输出均为20000000,耗时在3.8s左右

四:atomic原子操作代码

 1 //全局变量
 2 atomic<int> g_num = 0;
 3
 4 void fun()
 5 {
 6     for (int i = 0; i < 10000000; i++)
 7     {
 8         g_num++;
 9     }
10     return ;
11 }
12
13 int main()
14 {
15     //获取当前毫秒时间戳
16     typedef chrono::time_point<chrono::system_clock, chrono::milliseconds> microClock_type;
17     microClock_type tp1 = chrono::time_point_cast<chrono::milliseconds>(chrono::system_clock::now());
18     long long time1 = tp1.time_since_epoch().count();
19
20     //创建线程
21     thread t1(fun);
22     thread t2(fun);
23     t1.join();
24     t2.join();
25
26     cout << "总数:" << g_num << endl;
27
28     //获取当前毫秒时间戳
29     microClock_type tp2 = chrono::time_point_cast<chrono::milliseconds>(chrono::system_clock::now());
30     long long time2 = tp2.time_since_epoch().count();
31     cout << "耗时:" << time2 - time1 << "ms" << endl;
32
33     getchar();
34     return 1;
35 }

执行结果:多次测试输出均为20000000,耗时在1.3s左右

五:总结

  c++11的原子类atomic相比使用加锁机制性能有2~3倍提升,对于共享变量能用原子类型的就不要再用加锁机制了。

扫码关注公众号

专注分享Java,C/C++,STL,Spring框架,mybatis框架,mysql,redis,分布式,高并发,设计模式,爬虫,docker,shell编程等相关技术,在这里一起探讨,一起学习,一起进步,不定期分享视频书籍资源,充分利用碎片化时间,让我们的技术之路更加有乐趣。

原文地址:https://www.cnblogs.com/woniu201/p/10119583.html

时间: 2024-10-08 12:05:16

C++11并发编程:原子操作atomic的相关文章

C++11 并发编程基础(一):并发、并行与C++多线程

正文 C++11标准在标准库中为多线程提供了组件,这意味着使用C++编写与平台无关的多线程程序成为可能,而C++程序的可移植性也得到了有力的保证.另外,并发编程可提高应用的性能,这对对性能锱铢必较的C++程序员来说是值得关注的. 回到顶部 1. 何为并发 并发指的是两个或多个独立的活动在同一时段内发生.生活中并发的例子并不少,例如在跑步的时候你可能同时在听音乐:在看电脑显示器的同时你的手指在敲击键盘.这时我们称我们大脑并发地处理这些事件,只不过我们大脑的处理是有次重点的:有时候你会更关注你呼吸的

使用 C++11 并发编程入门

一.认识并发和并行 先将两个概念, 并发与并行 并发:同一时间段内可以交替处理多个操作: 图中整个安检系统是一个并发设计的结构.两个安检队列队首的人竞争这一个安检窗口,两个队列可能约定交替着进行安检,也可能是大家同时竞争安检窗口(通信).后一种方式可能引起冲突:因为无法同时进行两个安检操作.在逻辑上看来,这个安检窗口是同时处理这两个队列 并行:同一时刻内同时处理多个操作: 图中整个安检系统是一个并行的系统.在这里,每个队列都有自己的安检窗口,两个队列中间没有竞争关系,队列中的某个排队者只需等待队

[C++11 并发编程] 01 - Hello World

C++11标准支持了并发,其中包含了线程管理,共享资源保护,线程间同步操作和底层原子操作等功能.我们先通过一个简单的示例看看C++11标准的多线程程序是什么样的. #include <iostream> #include <thread> // 引用用于管理线程的类的头文件 using namespace std; // 线程的入口函数,程序将在新创建的线程中打印log void hello() { cout << "Hello Concurrent Worl

C++11并发编程

C++11开始支持多线程编程,之前多线程编程都需要系统的支持,在不同的系统下创建线程需要不同的API如pthread_create(),Createthread(),beginthread()等.现在C++11中引入了一个新的线程库,C++11提供了新头文件,主要包含 <thread>.<mutex>.<atomic>.<condition_varible>.<future>五个部分;<thread>等用于支持多线程,同时包含了用于启

[C++11 并发编程] 06 - Mutex 死锁

假设有两个线程,在执行某些操作时,都需要锁定一对mutex,线程A锁定了mutex A,而线程B锁定了额mutex B,它们都在等待对方释放另一个mutex,这就会导致这两个线程都无法继续执行.这种情况就是死锁. 避免死锁最简单的方法是总是以相同的顺序对两个mutex进行锁定,比如总是在锁定mutex B之前锁定mutex A,就永远都不会死锁. 假设有一个操作要交换同一个类的两个实例的内容,为了交换操作不被并发修改影响,我们需要锁定这两个实例内部的mutex.但是,如果选定一个固定的顺序来锁定

C++11并发编程入门

也许有人会觉得多线程和并发难用,复杂,还会让代码出现各种各样的问题,不过,其实它是一个强有力的工具,能让程序充分利用硬件资源,让程序运行得更快. 何谓并发: 两个或更多独立得活动同时发生.计算机中就是单个系统同时执行多个独立的任务,通过这个任务做一会儿,再切换到别的任务再做一会儿的方式,让任务看起来是并行执行的.切换就是做上下文切换,会有时间开销,操作系统为当前运行的任务保存CPU的状态和指针,计算出要切换到哪一个任务,并为即将切换到的任务重新加载处理器状态. 并发的方式: 多进程并发 多线程并

[C++11 并发编程] 06 Mutex race condition

上一节中介绍了mutex的基本使用方法,使用mutex来保护共享数据并不能解决race condition带来的问题,假如我们有一个堆栈数据结构类似于std::stack它提供了5个基本操作push(),pop(),top(),empty(),和size().这里的top()操作返回栈顶元素的拷贝,这样我们就可以使用一个mutex来保护栈内部的数据.但是race codition情况下,虽然使用mutex在stack的每个接口内都对共享数据进行了保护,仍然有问题存在. #include <deq

[C++11 并发编程] 04 动态选择并发线程的数量

C++标准模板库提供了一个辅助函数 - std::thread::hardware_concurrency(),通过这个函数,我们可以获取应用程序可以真正并发执行的线程数量.下面这个例子,实现了一个并发版本的std::accumulate,它将工作拆分到多个线程中,为了避免过多线程带来的开销,程序指定了每个线程处理数据的最小数量. 头文件和求和操作: #include <thread> #include <numeric> #include <algorithm> #i

[C++11 并发编程] 08 - Mutex std::unique_lock

相对于std::lock_guard来说,std::unique_lock更加灵活,std::unique_lock不拥有与其关联的mutex.构造函数的第二个参数可以指定为std::defer_lock,这样表示在构造unique_lock时,传入的mutex保持unlock状态.然后通过调用std::unique_lock对象的lock()方法或者将将std::unique_lock对象传入std::lock()方法来锁定mutex. #include <mutex> class some