++i和i++是线程安全的吗

       显然不是。

       

       ++i和i++的区别在于一个是先自增再赋值,一个是先赋值再自增。(大家应该都知道,不详细举例子了)

       
       ++i和i++的过程可以分为3步, 这3步并不是一个原子操作, 一个线程在执行①、②、③步的过程中,是可能被其他线程打断的:

  • ①从内存读入临时i值,假设为temp,放在寄存器
  • ②在寄存器中对temp值+1
  • ③将寄存器中temp+1的值写回内存赋给i

       举个例子,假设现在i是一个全局变量,有2个线程要各自同时对i自增1000w次。代码如下:

#include <iostream>
#include <vector>
#include <thread>
using namespace std;
int i = 0;	  // 全局变量
void fun() {
	for (int j = 0; j < 10000000; ++j) {
		++i;
	}
}
int main() {
	vector<thread> vec;
	for (int j = 0; j < 2; ++j) {
		vec.emplace_back(fun);
		vec[j].detach();		// 2个线程同时执行fun()函数对i进行自增
	}	
	this_thread::sleep_for(chrono::seconds(5));	// 等待2个线程运行结束
	cout << i << endl;	
}

        以上代码就可能出现这样的问题:
        假设此时i值为100,线程1在进行了上面的①、②后,正准备进行③将101的值赋给i, 但是, 在线程1进行①、②步骤后,可能CPU分配给线程1的时间片用完了,现在CUP开始执行线程2,线程2对值为100的i进行自增,假设已经增加到了200, 然后, 线程2时间片用完,CPU接着执行线程1, 这个时候问题就来了, 线程1会将先前保存的temp+1值,也就是101赋给i,然后i本应该是201的,现在值却只有101。
        因此上面代码的输出结果,本应该是2* 1000w,但运行结果可能会比2* 1000w小,而且每次运行的结果可能不同,因为多个线程之间存在竞争关系。
在这里插入图片描述

       

       

       如何让以上程序达到预期效果,也就是一个线程在对全局变量进行操作的时候不被其他线程打断(或者说多个线程互斥地访问共享资源)呢?可以采用加锁的方法,对程序修改如下:

#include <iostream>
#include <vector>
#include <thread>
#include <mutex>
using namespace std;
int i = 0;
mutex m;	// 全局锁
void fun() {	
	for (int j = 0; j < 10000000; ++j) {
		m.lock();
		i++;		// 对i++操作进行加锁
		m.unlock();
	}	
}
int main(){
	vector<thread> vec;
	for (int j = 0; j < 2; ++j) {
		vec.emplace_back(fun);
		vec[j].detach();		
	}
	// 休眠30秒等待2个线程运行结束,不等它们运行结束(解锁)就去访问被上锁了的i值,会报错"mutex destroyed while busy"
	this_thread::sleep_for(chrono::seconds(30));	
	cout << i << endl;		
}

  
  

       但是,以上程序的运行时间会很长,因为每进行一个自增操作,都要加锁、解锁,上面的程序要加锁解锁2*1000w次!我们知道,加锁解锁是有时间开销的,这样频繁地加锁会消耗很长的时间。

       可以改变锁住的区域,将上面的程序改为:

#include <iostream>
#include <vector>
#include <thread>
#include <mutex>
using namespace std;
int i = 0;
mutex m;	// 全局锁
void fun() {
	m.lock();	
	for (int j = 0; j < 10000000; ++j) {		
		i++;		// 对i++操作进行加锁		
	}
	m.unlock();	
}
int main(){
	vector<thread> vec;
	for (int j = 0; j < 2; ++j) {
		vec.emplace_back(fun);
		vec[j].detach();		
	}
	// 休眠10秒等待2个线程运行结束,不等它们运行结束(解锁)就去访问被上锁了的i值,会报错"mutex destroyed while busy"
	this_thread::sleep_for(chrono::seconds(10));	
	cout << i << endl;		
}

       这样一共只需要进行2次加锁和解锁。但是,这样等同于让线程1和线程2串行执行,线程1在没有完成1000w次自增时不会解锁,那么线程2执行时无法获得该互斥锁,就会被阻塞起来,直到线程1释放锁。这种情况下,假设计算机有多个核,本可以将两个线程放在不同核上并行执行的,这样加锁使两个线程串行执行,也快不到哪里去。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值