我们知道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可实现数据结构的无锁设计。