C++多线程的原子操作、线程阻塞

13 篇文章 0 订阅
4 篇文章 0 订阅

头文件#include <Thread>及原子操作

在C++11中,<Thread>头文件包含了Thread类,提供线程的管理。

原子操作:不可被中断的一个或者一系列操作,这些操作要一次性执行完毕,或者一个都不执行。




多线程存在的问题

在多线程中,由于进程的多个线程都是共享该进程的所有资源,那么如果有多个线程访问同一个资源时,可能会出现问题。

如果多个线程都是只读操作,那么数据不会被修改,每个进程读到的数据都是一致的,得到的结果自然也是正确的;如果涉及到数据的修改,那么多个进程读取的结果可能会出现意想不到的情况

Demo

#include <iostream>
#include <thread>
using namespace std;

uint64_t sum = 0;
 
void func(){
    for(uint64_t i = 0; i < 100000; i++)
        sum += i;
}
 
int main(){
    cout << "Start:\tsum = " << sum << endl;
    
	thread th1(func);
    thread th2(func);
    th1.join();
    th2.join();
    
    cout << "End:\tsum = " << sum << endl;
    
   	return 0;
}

代码和运行结果分析:

代码分析
在func中实现从0到99999的累加,在main()中实例化两个线程,调用func,理论上得到的结果应该为9999900000。实际运行结果如下:
在这里插入图片描述

结果分析
尝试几次运行后发现每次的结果都不一样,并且都不是想要的结果。

因为对sum的修改并不是原子操作,两个线程th1和th2都在同时修改sum
在机器指令层面,sum += i操作被分割成两步①先保存i的值到寄存器,②再将该寄存器的值+sum,然后保存到sum中。
在两步的间隙中,多线程的问题便体现出来。




原子操作

在C++11中,加入了新的头文件<atomic>,这是一个模板类,其中定义了数据类型以及重载操作符等。

之前定义的sum类型是uint64_t,即unsigned long long类型,在atomic头文件包含的头文件<atomic_basic.h>中,

	...
	/// atomic_uint
	typedef __atomic_base<unsigned int>	     	atomic_uint;

	/// atomic_long
	typedef __atomic_base<long>  	       		atomic_long;

	/// atomic_ulong
	typedef __atomic_base<unsigned long>		atomic_ulong;

	/// atomic_llong
	typedef __atomic_base<long long>  		atomic_llong;

	/// atomic_ullong
	typedef __atomic_base<unsigned long long> 	atomic_ullong;
	...

所以用新的类型atomic_ullong声明sum变量。

修改后的demo

#include <iostream>
#include <thread>
#include <atomic>
using namespace std;

atomic_ullong sum = {0};
 
void func(){
    for(uint64_t i = 0; i < 100000; ++i)
        sum += i;
}
 
int main(){
    cout << "Start: sum = " << sum << endl;
    
	thread th1(func);
    thread th2(func);
    th1.join();
    th2.join();
    
    cout << "End: sum = " << sum << endl;
    
   	return 0;
}

运行结果如下:
在这里插入图片描述
因为sum变量声明atomic,所以为原子操作,多个线程之间不会干扰,结果正确

修改方案2

#include <iostream>
#include <thread>
using namespace std;

uint64_t sum = 0L;
 
void func(){
    for(uint64_t i = 0; i < 100000; i++)
        sum += i;
}
 
int main(){
    cout << "Start:\tsum = " << sum << endl;
    
    thread th1(func);
    th1.join();
    thread th2(func);
    th2.join();
    
    cout << "End:\tsum = " << sum << endl;
    
   	return 0;
}

对两个线程分别用join操作,也可以得到正确的结果。
这里没有用原子操作,而是用线程的阻塞,保证th1线程在执行的过程中,th2无法访问th1所占用的资源。最终可以得到正确的结果。

join操作的阻塞相关参考:https://blog.csdn.net/hl_zzl/article/details/84637415

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值