显然不是。
++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释放锁。这种情况下,假设计算机有多个核,本可以将两个线程放在不同核上并行执行的,这样加锁使两个线程串行执行,也快不到哪里去。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/157139.html