我们知道const成员函数不会修改成员变量,即对变量进行只读操作,但是即使是只读,其函数真的是安全的吗?例如,我们在一个成员函数内获取某个对象的一个变量的值,其值在第一次和第二次读取有差异,代码如下:
class MyClass
{
public:
void Init() const {
if (!InitFlag) {
//.... 执行一系列操作
InitFlag = true;
}
}
private:
mutable bool InitFlag = false;//mutable用法后续会将
};
上面可以看出,作用就是对一个对象进行初始化,但是如果此时两个线程同时调用同一个对象的Init()函数会发生什么呢?很明显,这个不符合代码设计的预期。所以这段代码没有线程安全性。
保证线程安全性最简单的方法就是借助互斥量std::mutex。代码修改如下:
class MyClass
{
public:
void Init() const {
std::lock_guard<std::mutex> g(m);//加上互斥量,另外线程想要访问时,将阻塞
if (!InitFlag) {
//.... 执行一系列操作
InitFlag = true;
}
}
private:
mutable std::mutex m; //m必须为mutable,加锁解锁会改变m的值
mutable bool InitFlag = false;//mutable用法后续会将
};
这里引入互斥量有点杀鸡用牛刀的意思,如果只是对单个变量进行原子操作时,可以借助std::atomic<T>,std::atomic对int, char, bool等数据结构进行原子性封装,在多线程环境中,对std::atomic对象的访问不会造成竞争-冒险。利用std::atomic可实现数据结构的无锁设计。
要点速记
- 保证const成员函数的线程安全性,除非可以确信其不会出现在并发语境下
- 运用std::atomic型别的变量会比运行互斥量有更好的性能,但std::atomic仅适用于单个变量的操作