双重检查锁定模式'在这种情况下对std :: mutex有好处吗?

时间:2014-04-02 07:46:47

标签: c++ c++11

我经常遇到这个线程安全结构的设计。如下面的 version1 ,一个线程可能很少调用foo1::add_data(),另一个线程通常调用foo1::get_result()。出于优化的目的,我认为它可以使用原子来应用双重检查锁定模式(DCLP),如 version2 所示。这种情况还有其他更好的设计吗?或者它是否可以改进,例如使用std::memory_order

访问原子

VERSION1

class data {};
class some_data {};
class some_result {};

class foo1
{
public:
    foo1() : m_bNeedUpdate(false) {}

    void add_data(data n)
    {
        std::lock_guard<std::mutex> lock(m_mut);

        // ... restore new data to m_SomeData

        m_bNeedUpdate = true;
    }

    some_result get_result() const
    {
        {
            std::lock_guard<std::mutex> lock(m_mut);
            if (m_bNeedUpdate)
            {
                // ... process mSomeData and update m_SomeResult

                m_bNeedUpdate = false;
            }
        }
        return m_SomeResult;
    }

private:
    mutable std::mutex  m_mut;
    mutable bool        m_bNeedUpdate;
    some_data           m_SomeData;

    mutable some_result m_SomeResult;
};

版本2

class foo2
{
public:
    foo2() : m_bNeedUpdate(false) {}

    void add_data(data n)
    {
        std::lock_guard<std::mutex> lock(m_mut);

        // ... restore new data to m_SomeData

        m_bNeedUpdate.store(true);
    }

    some_result get_result() const
    {
        if (m_bNeedUpdate.load())
        {
            std::lock_guard<std::mutex> lock(m_mut);
            if (m_bNeedUpdate.load())
            {
                // ... process mSomeData and update m_SomeResult

                m_bNeedUpdate.store(false);
            }
        }
        return m_SomeResult;
    }

private:
    mutable std::mutex          m_mut;
    mutable std::atomic<bool>   m_bNeedUpdate;
    some_data                   m_SomeData;

    mutable some_result         m_SomeResult;
};

3 个答案:

答案 0 :(得分:2)

问题是版本2至少不是线程安全的 根据C ++ 11(和Posix,之前的);你正在访问 可以在没有访问权限的情况下修改的变量 保护。 (双重检查锁定模式是已知的 坏了,看 http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf)。 它可以在C ++ 11中工作(或者不可移植地更早) 使用原子变量,但你写的是什么 未定义的行为。

答案 1 :(得分:1)

我认为通过使用允许许多线程并行读取的“读写锁”,可以实现显着的改进(在代码大小方面以及在简单性和性能方面)。为此目的,Boost提供了shared_mutex,但从快速浏览看来,this blog article以可移植的方式实现了相同类型的锁定而不需要Boost。

答案 2 :(得分:0)

你说过你经常打电话给get_average,你是否考虑过根据你没见过的数字来计算平均值?&#39;?它将是O(n)而不是O(n ^ 2)。

这就像是

average = (last_average * last_size + static_cast<double>(
           std::accumulate(m_vecData.begin() + last_size, m_vecData.end(), 0))) /
           m_vecData.size();

它应该给你满意的结果,取决于你的矢量有多大。