问题
#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++中提供了原子操作可以实现这一目标。
代码如下:
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()
1.本站内容仅供参考,不作为任何法律依据。用户在使用本站内容时,应自行判断其真实性、准确性和完整性,并承担相应风险。
2.本站部分内容来源于互联网,仅用于交流学习研究知识,若侵犯了您的合法权益,请及时邮件或站内私信与本站联系,我们将尽快予以处理。
3.本文采用知识共享 署名4.0国际许可协议 [BY-NC-SA] 进行授权
4.根据《计算机软件保护条例》第十七条规定“为了学习和研究软件内含的设计思想和原理,通过安装、显示、传输或者存储软件等方式使用软件的,可以不经软件著作权人许可,不向其支付报酬。”您需知晓本站所有内容资源均来源于网络,仅供用户交流学习与研究使用,版权归属原版权方所有,版权争议与本站无关,用户本人下载后不能用作商业或非法用途,需在24个小时之内从您的电脑中彻底删除上述内容,否则后果均由用户承担责任;如果您访问和下载此文件,表示您同意只将此文件用于参考、学习而非其他用途,否则一切后果请您自行承担,如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。
5.本站是非经营性个人站点,所有软件信息均来自网络,所有资源仅供学习参考研究目的,并不贩卖软件,不存在任何商业目的及用途
暂无评论内容