0 结论
- 保证const成员函数的线程安全,确定它们不会用在并发的语境中;
- 运用
std::atomic 类型的变量会比互斥类型好,但std::atomic 仅适用于单个变量或内存区域的操作。
1 论据
当存在数据竞险(由多个线程在没有同步的情况下读写同一块内存)时,
- 如果单个要求的同步的变量或内存区域,使用
std::atomic 就足够了; - 如果有两个或更多变量或内存区域需要作为一整个单位进行操作时,就要用到互斥变量mutex。
1.1 使用std::atomic
以较小的开销运行。
class TestConst{
public:
double testFunc() const noexcept{
++callCount;
std::cout<<callCount<<std::endl;
return x*y;
}
private:
mutable std::atomic<unsigned > callCount{0};
double x{2}, y{3};
};
#include<thread>
int main(){
TestConst* p = new TestConst();
std::thread t(&TestConst::testFunc, p);
std::thread t2(&TestConst::testFunc, p);
t.join();
t2.join();
delete p;
}
如果callCount没有mutable的声明,那么在const函数函数中,就会当作const对象处理。
2 使用互斥变量mutex
由于std::mutex 是只移动类型(只能移动不能复制),那么它修饰的变量也将失去复制性。
当使用std::atomic时,
class TestConst{
public:
int cal() const{
return 2;
}
int testFunc() const{
if (cacheVaild){
return cacheVaild;
}else{
auto v1 = cal();
auto v2 = cal();
cacheVaild = true;
return cacheValue = v1+v2;
}
}
private:
mutable std::atomic<bool> cacheVaild{false};
mutable std::atomic<int> cacheValue{0};
};
可能出现的问题为:当一个线程调用testFunc()并执行到cacheVaild被置为true,这时另一个另一线程也在调用testFunc(),在其观察到cacheVaild值后,就直接返回了一个没有赋值的cacheValue。
解决方法:
class TestConst{
public:
int cal() const{
return 2;
}
int testFunc() const{
std::lock_guard<std::mutex> guard(m);
if (cacheVaild){
return cacheVaild;
}else{
auto v1 = cal();
auto v2 = cal();
cacheVaild = true;
return cacheValue = v1+v2;
}
}
private:
mutable std::mutex m;
mutable bool cacheVaild{false};
mutable int cacheValue{0};
};
|