C++原子操作与内存序 1

问题

c++ 复制代码
#include<iostream>
#include<thread>
int main()
{
	int sum = 0;
	auto f = [&sum]() {
		for (int i = 0; i < 10000; i++)
			sum += 1;
	};
	std::thread t1(f);
	std::thread t2(f);

	t1.join();
	t2.join();
	std::cout << "the sum of 2 threads is: " << sum << std::endl;
	std::cin.get();
	return 0;
}

这个程序只是简单的通过两个线程对同一个变量进行累加10000次,正常不管线程执行的先后顺序,结果都应该是20000才对,可实际输出结果如图所示,程序的输出3次的结果都不一样,不一定是预期的20000;

分析

对于+1操作,具体执行可以分为3个操作,如下图所示:

可以看出问题发生在两个线程写的时候,如线程1刚写完,线程2继续写,则丢失一次加法。所以得出的值往往小于20000。

解决

可以通过std::mutex加锁对变量操作进行保护,有没有不用锁也能实现的呢?C++中提供了原子操作可以实现这一目标。

代码如下:

C++ 复制代码
	std::atomic<int> sum1 = 0;
	auto f1 = [&sum1]() {
		for (int i = 0; i < 10000; i++)
			sum1+=1;
	};

	std::thread t3(f1);
	std::thread t4(f1);

	t3.join();
	t4.join();
	std::cout << "the sum of 2 threads with atomic is: " << sum1 << std::endl;

输出如下:

可以看出未原子化的sum仍然是每次结果不尽相同,而原子化的sum1每次结果都为20000。

所谓原子操作指的是不可分割的操作,可以理解为只能编译成一条单独的CPU执行指令,不可以再分解,C++中,基本通过原子类型来实现原子操作。这种原子类型为std::atomic<T>,其中模板参数T为基本的数据类型,如bool,char,int,指针等。

程序中将sum1原子化,并调用+=操作符(已重载为原子操作),之前分解的3步成了不可分割的1步,所以不会出现两个线程同时已经进入写的状态,进而能保证累加结果的正确。

注意事项

  • 若累加操作改为sum1=sum1+1,就不是原子操作了,结果与sum没有差别

int型++/+=是原子操作fetch_add()的重载,类似的还有fetch_sub()/fetch_and/fetch_or()/fetch_xor()

相关推荐
yuanbenshidiaos1 分钟前
C++----------函数的调用机制
java·c++·算法
tianmu_sama25 分钟前
[Effective C++]条款38-39 复合和private继承
开发语言·c++
羚羊角uou40 分钟前
【C++】优先级队列以及仿函数
开发语言·c++
姚先生9744 分钟前
LeetCode 54. 螺旋矩阵 (C++实现)
c++·leetcode·矩阵
FeboReigns1 小时前
C++简明教程(文章要求学过一点C语言)(1)
c语言·开发语言·c++
FeboReigns1 小时前
C++简明教程(文章要求学过一点C语言)(2)
c语言·开发语言·c++
264玫瑰资源库1 小时前
从零开始C++棋牌游戏开发之第二篇:初识 C++ 游戏开发的基本架构
开发语言·c++·架构
_小柏_2 小时前
C/C++基础知识复习(43)
c语言·开发语言·c++
264玫瑰资源库2 小时前
从零开始C++棋牌游戏开发之第四篇:牌桌界面与交互实现
开发语言·c++·交互
pzx_0013 小时前
【LeetCode】LCR 175.计算二叉树的深度
开发语言·c++·算法·leetcode·职场和发展