我經常遇到這個線程安全結構的設計。如下面的版本1,一個線程可能很少會調用foo1::add_data()
,而另一個線程經常會調用foo1::get_result()
。爲了優化,我認爲它可以使用原子來應用雙重檢查鎖定模式(DCLP),如版本2所示。對於這種情況,還有其他更好的設計嗎?或者可以改進它,例如使用std::memory_order
訪問原子?在這種情況下,'雙重檢查鎖定模式'是否適合std :: mutex?
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;
};
除了'm_bNeedUpdate',您還需要對'm_SomeResult'進行原子訪問,否則就會出現競爭(線程1開始複製'm_SomeResult',線程2中斷它並開始更新)。假設'm_SomeResult'是不可或缺的,只需使它成爲原子;否則,你是恕我直言更好的標準讀寫鎖(我認爲它可以用雙重檢查鎖,但它很複雜,不值得)。 –
如果只有一個線程調用'foo2 :: get_result()',它仍然是競爭條件嗎? – cbel
可能不是這種情況。 –