多线程下难免会需要资源共享,这样难免会发生异常情况。
#include <iostream>
#include <thread>
class X
{
int* _p;
public:
X(int* p = nullptr) : _p(p) {
}
~X() {
if(_p) delete _p; }
void read()
{
if(_p) std::cout << "value: " << *_p << std::endl;
}
void reset()
{
if(_p) {
delete _p; // 2
_p = nullptr; // 3
}
}
};
int main()
{
X x(new int(77));
std::thread t1(&X::read, &x), t2(&X::reset, &x);
t1.join();
t2.join();
return 0;
}
编译运行这个例子可以发现有时代码正常运行输出,而有时却发生了段错误。原因在于指针_p被两个线程共享,会有三种情况:
- t1 线程已执行过访问_p,t2线程还未执行delete _p,那么就正常输出value: 77
- t1线程执行完判断还未访问_p就被挂起,t2线程调度成功执行完delete _p,然后t2线程也被挂起,t1线程唤醒继续执行访问 _p造成段错误
- t2线程执行完了_p = nullptr,t1线程判断 _p,那么就没输出,不过概率不高
- 还要种情况就留给读者自己思考了,概率也不是很高,主要还是知道会发生异常就行了
所以需要一种方法使得t1线程访问_p时,t2线程不得对 _p就行操作;同理,t2操作时,t1不得读取。
1. 使用互斥量
C++11 提供了互斥量来保护共享数据,思路是线程A在操作共享数据前上锁互斥量,其他线程执行到上锁的互斥量时就会进入阻塞,只有线程A解锁互斥量时, 其他某个线程才能继续上锁,然后那个线程继续执行后续代码。
#include <mutex>
// 互斥量类,不可移动也不可否则
class std::mutex;
void std::mutex::lock();
// 对互斥量进行上锁, 一次只能有线程成功调用此方法并返回,其他线程执行此方法就会进入阻塞状态, 直到unlock方法调用
void std::mutex::unlock();
// 对lock的互斥量继续解锁,以便其他线程可以解除阻塞而返回
bool std::mutex::try_lock();
// 尝试进行上锁操作。若无其他线程上锁,那么上锁互斥量,返回true;若互斥量也被上锁,那么上锁失败返回false
std::mutex::native_handle_type std::mutex::native_handle();
// 返回互斥量的底层标识符, 可用于扩展,但不建议这样用
#include <mutex>
class X
{
int* _p;
std::mutex mutex;
public:
X(int* p = nullptr