c++11线程安全
多线程安全问题
在多个线程中共享数据时,需要注意线程安全问题。如果多个线程同时访问同一个变量,并且其中至少有一个线程对该变量进行了写操作,那么就会出现数据竞争问题。数据竞争可能会导致程序崩溃、产生未定义的结果,或者得到错误的结果。
代码如下
多线程间数据竞争代码示例:
#include <iostream>
#include <thread>
using namespace std;int shared_data = 0;
void func()
{for (int i = 0; i < 100000; i++){shared_data += 1;}
}
int main()
{for (int i = 0; i < 5; i++){shared_data = 0;thread t1(func);thread t2(func);t1.join();t2.join();cout << "第" <<i+1<<"次执行的" << "shared_data值为:" << shared_data << endl;}return 0;
}
打印的结果如下:
按理说,线程t1将shared_data 加100000次,线程t2执行将shared_data 加100000次,执行的结果应该时200000。但是测试了5次,每一次的打印结果都小于200000,这是因为多线程间访问出现了数据竞争问题。
以下是结合上述代码绘制的关于数据竞争的流程图
多核状态下:
单核状态下:
如果只有一个cpu内核,那么某一时刻只能有一个线程在执行,更理想的情况,如果是只有t1和t2两个线程在执行,t1执行完毕就t2执行,感觉不会发生数据竞争唉。
其实不是,以下图为例,比如说线程t1刚好读入数据shared_data的值,恰好这个时候t1的时间片用完了;轮到线程t2执行,t2执行读取shared_data的数据循环+1,比如说加到610,t2的时间片到了,将610写入内存shared_data;随后切换到t1线程,这里理论上shared_data的值该是610了,但是实际上t1中保存的上次执行值为10,于是线程t1在10的基础上进行循环+1,再次加到610,并将值写入shared_data中。但是实际上经过这两个线程的时间片执行,其值应该为1210(假设一个时间片内执行了600次加法操作)。
线程安全
概念:如果多线程中每一次的运行结果和单线程运行的结果始终是一样的,那么这个就可以叫做线程安全。
理解:在这里代码的理解就是线程t1执行了100000次加法操作,t2执行了100000次加法操作,最终的值就应该是200000,并且无论执行多少次,其值始终为200000。
为了避免数据竞争问题,也是为了实现线程安全,需要使用同步机制来确保多个线程之间对共享数据的安全访问。常见的同步机制包括互斥量、条件变量、原子操作等。
互斥量机制
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;int shared_data = 0;
mutex mtx;
void func()
{for (int i = 0; i < 100000; i++){mtx.lock();shared_data += 1;mtx.unlock();}
}
int main()
{for (int i = 0; i < 5; i++){shared_data = 0;thread t1(func);thread t2(func);t1.join();t2.join();cout << "第" <<i+1<<"次执行的" << "shared_data值为:" << shared_data << endl;}return 0;
}
打印结果为:
可以看到,执行了5次打印结果都是200000,通过互斥量实现了线程安全。
原子操作
使用atomic原子操作也可以实现线程安全。
#include <iostream>
#include <thread>
#include <atomic>
using namespace std;atomic<int> shared_data = 0;void func()
{for (int i = 0; i < 100000; i++){shared_data += 1;//shared_data.fetch_add(1);}
}
int main()
{for (int i = 0; i < 5; i++){shared_data = 0;//shared_data.store(0);thread t1(func);thread t2(func);t1.join();t2.join();cout << "第" << i + 1 << "次执行的" << "shared_data值为:" << shared_data << endl;}return 0;
}
运行结果为:
上述打印结果几次的执行结果都是一样的,说明原子变量atomic实现了线程安全。