[英]Is 'double checked locking pattern' good for std::mutex in this situation?
我經常遇到這種線程安全結構的設計。 如下面的version1 ,一個線程很少調用foo1::add_data()
,而另一個線程經常調用foo1::get_result()
。 出於優化的目的,我認為它可以使用原子來應用雙重檢查鎖定模式(DCLP),正如版本2所示。 這種情況還有其他更好的設計嗎? 或者它是否可以改進,例如使用std::memory_order
訪問原子?
版本1 :
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;
};
問題是版本2不是線程安全的,至少根據C ++ 11(和Posix,之前的版本); 您正在訪問一個變量,該變量可以在不受訪問受保護的情況下進行修改。 (已知雙重檢查的鎖定模式已被破壞,請參閱http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf 。) 可以使用C ++ 11(或前面的非移植版本)使用它原子變量,但你寫的東西導致未定義的行為。
我認為通過使用允許許多線程並行讀取的“讀寫鎖”,可以實現顯着改進(在代碼大小以及簡單性和性能方面)。 Boost為此目的提供了shared_mutex
,但是從快速瀏覽看來, 這篇博客文章似乎以可移植的方式實現了相同類型的鎖,而不需要Boost。
你說你經常調用get_average,你是否考慮過根據你沒有“看到”的數字計算平均值? 它將是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();
它應該給你滿意的結果,取決於你的矢量有多大。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.