线程安全且独特的嵌套函数执行

时间:2016-07-12 09:27:59

标签: c++ multithreading c++11

我有一个类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";
        }
};

2 个答案:

答案 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;
};

Online Demo

请注意,任何涉及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);
        }
    }
}