1 原子操作std::atomic相关概念
前言:
- 原子操作:更小的代码片段,并且该片段必定是连续执行的,不可分割。
1.1 原子操作std::atomic与互斥量的区别
- 1)互斥量:类模板,保护一段共享代码段,可以是一段代码,也可以是一个变量。
- 2)原子操作std::atomic:类模板,保护一个变量。
1.2 为何需要原子操作std::atomic
上面可以看到,为何已经有互斥量了,还要引入std::atomic呢,这是因为互斥量保护的数据范围比较大,我们期望更小范围的保护。并且当共享数据为一个变量时,原子操作std::atomic效率更高。
我们看简单分析几行代码:
int g = 0;//有一全局变量
//假设线程A不断读该变量
int t = g;
//线程B不断写
g++;
//那么我们知道,g++这一行是由许多行汇编代码组成的,
//当我在写入的过程中,A线程刚好读,那么读出来的值就可能存在不确定,
//所以必须保护该共享数据,可以是互斥量或者优选std::atomic
2 代码测试
2.1 测试当对一个共享变量不加保护时的结果
注意:多个线程对int这种全局变量同时写入时不会发生异常或者崩溃,只会造成数据紊乱。而对于容器(迭代器)这些,由于编译器明确其合法性和抛出对应异常,多个线程同时写入是会造成程序异常和崩溃的。
#include<iostream>
#include<thread>
#include<string>
#include<vector>
#include<list>
#include<mutex>
#include<future>
using namespace std;
int g = 0;
void mythread(){
for (int i = 0; i < 2000000; i++) {
g++;
}
}
int main(){
thread t1(mythread);
thread t2(mythread);
t1.join();
t2.join();
cout << "g的值为:" << g << endl;
return 0;
}
当我运行上面代码多次时,结果几乎都是小于200000两百万的,实际上是一定小于两百万的(不考虑程序异常),这个有的公司面试时会问到原因。
因为A写入的过程中,B也刚好读到A写入之前的值,那么就造成加了两次,结果值只加了1。例如AB同时读到了2,那么它们都加1了,但是结果为3。并且同时写入时,也可能会出现读到一个未确定的值(概率极其小,程序异常可能会出现),造成写入的结果非常大或者非常小。所以这就很有必要引入原子操作std::atomic,进而保护对应的汇编代码为原子操作。
例如下面结果是:1800030。
2.2 使用原子操作std::atomic
代码非常简单,将上面的代码改成这样即可。这样无论你运行多少次,结果都是两百万。
//int g = 0;
std::atomic<int> g = 0;
2.3 操作bool类型的std::atomic用法
#include<iostream>
#include<thread>
#include<string>
#include<vector>
#include<list>
#include<mutex>
#include<future>
using namespace std;
//int g = 0;
//std::atomic<int> g = 0;
std::atomic<bool> g = false;
void mythread(){
while (g == false) {
cout << "mythread运行中,tid= " << this_thread::get_id() << endl;
this_thread::sleep_for(chrono::seconds(1));
}
}
int main(){
thread t1(mythread);
thread t2(mythread);
//睡眠5秒让子线程都退出循环
this_thread::sleep_for(chrono::seconds(5));
g = true;
t1.join();
t2.join();
cout << "g的值为:" << g << endl;
return 0;
}
结果:
3 原子操作std::atomic支持的运算符
2.1 测试支持的运算符
我们将2.2(省略了,即2.1修改int成std::atomic即可)的原子操作代码拿来测试。
- 1)测试g = g + 1;
- 2)测试g += 1;
- 多次运行皆为两百万,支持。
2.2 总结std::atomic支持的运算符
- 1)一般atomic原子操作,针对++、–、+=、&=、|=是支持的。其他的可能不支持。
3 简述原子操作std::atomic的用途
- 1)一般用户统计。例如统计一共发送了多少数据包,接收了多少个数据包。new了多少次,delete了多少次。