如何正确序列化线程访问控制循环的标志

How to correctly serialise thread access to a flag controlling a loop

我有一个函数 f1,它包含一个简单的循环,它通过布尔标志进行控制。该标志未写入 f1 内部。

我还有一个清除标志的函数。

这两个函数是在不同的线程上调用的。

如果我在 f1 进入循环之前锁定互斥量,那么 f2 将无法获取它以清除标志。

如果我在进入 f1 中的循环之前不锁定互斥体,则该标志不受保护。考虑到函数只读取它,这有关系吗?

我的问题是我是否需要在进入 f1 循环之前保护标志,因为它只被读取?如果是这样,如何?

如果标志只写入一个地方,我还需要互斥体吗?

我是否遗漏了一些基本的东西?

TIA

class X
{
public:
    X() :
        m_thread(),
        m_isDone(false),
        m_mutex()
    {
        m_thread = std::unique_ptr<std::thread>(new std::thread( [=]{ run(); } ));
    }

    ~X()
    {
        // tell the thread to exit
        m_isDone = true;

        // wait for the thread to terminate
        m_thread->join();
    }

    void f1()
    {
        // locking the mutex will prevent f2 from clearing the flag
        std::lock_guard<std::mutex> lock(m_mutex);

        while (!m_isDone)
        {
            // do some stuff
        }
    }

    void f2()
    {
        // lock the mutex
        std::lock_guard<std::mutex> lock(m_mutex);
        m_isDone = true;
    }

private:
    std::unique_ptr<std::thread> m_thread;
    bool m_isDone;
    mutable std::mutex m_mutex;
};

只需更改:

bool m_isDone;

至:

std::atomic<bool> m_isDone;

这将使您对 m_isDone 的读写保证原子性(您需要这样做,因为它是在不同的线程中读写的)。 atomic 也消除了对任何类型的互斥锁、锁等的需要。

请注意,在您的解决方案中,f1 持有锁 永远 ,因此 f2 永远无法获得它。带锁的正确解决方案是更复杂和不必要的:

bool isDone() { // easily replaced by atomic load
    std::lock_guard<std::mutex> lock(m_mutex);
    return m_isDone;
}

void f1() {
    while (!isDone()) {
        // stuff
    }
}