我有一个类MyClass
,其函数A
并行执行多次。然后,有一个函数B
只能执行一次。我的初始设置看起来很简单,但我怀疑它是线程安全的。如何使其线程安全?我正在使用C ++ 11。
class MyClass {
public:
void A() {
static bool execute_B = true;
if (execute_B) {
execute_B = false;
B();
}
}
private:
void B() {
std::cout << "only execute this once\n";
}
};
答案 0 :(得分:3)
这是std::atomic_flag
的主要用例:
class MyClass {
public:
void A() {
if (!execute_B_.test_and_set()) {
B();
}
}
private:
void B() {
std::cout << "only execute this once\n";
}
std::atomic_flag execute_B_ = ATOMIC_FLAG_INIT;
};
请注意,任何涉及static
的解决方案只允许调用MyClass::B
,即使是多个MyClass
个实例,这对您来说可能有意义,也可能没有意义;假设它没有有意义,这种方法允许每个 MyClass::B
实例调用MyClass
。
答案 1 :(得分:2)
是的,您的代码不是安全的:在if
将被设置为false之前,几个线程可以进入execute_B
语句体内。此外,execute_B
不是原子的,因此您可能会遇到线程之间更改可见性的问题。
有许多方法可以使其成为线程安全的。请注意,版本(1),(2)和(4)将阻止其他线程执行A
超过B
执行点,直到B
执行完成。
1)已经提到std::call_once
:
void A() {
static std::once_flag execute_B;
std::call_once(flag1, [this](){ B(); });
}
2)调用B
作为初始化静态变量的结果:
void A() {
static bool dummy = [this](){ B(); return true; });
}
3)使用原子交换:
void A() {
static std::atomic<bool> execute_B = true;
if(execute_B.exchange(false, std::memory_order_acq_rel))
B();
}
4)使用互斥锁保护检查(以避免以后出现性能下降,使用双重检查锁定):
void A() {
static std::mutex m_;
static std::atomic<bool> execute_B = true;
if(execute_B.load(std::memory_order_acquire)) {
std::unique_lock<std::mutex> lk(m_);
if(execute_B.load(std::memory_order_relaxed)) {
B();
execute_B.store(false, std::memory_order_release);
}
}
}