基于previous question,我想知道以下代码是否可以计算使用原子测量的属性的平均值的下限和上限:
std::atomic< unsigned int > m_accLower;
std::atomic< unsigned int > m_countLower;
std::atomic< unsigned int > m_accUpper;
std::atomic< unsigned int > m_countUpper;
// ...
void Class::UpdateLower( unsigned int delta )
{
m_countLower.fetch_add( 1 , std::memory_order_relaxed );
m_accLower.fetch_add( delta , std::memory_order_release );
}
double Class::GetAverageLower( )
{
auto acc = m_accLower.load( std::memory_order_acquire );
auto count = m_countLower.load( std::memory_order_relaxed );
return acc/(double)count;
}
void Class::UpdateUpper( unsigned int delta )
{
m_accUpper.fetch_add( delta , std::memory_order_relaxed );
m_countUpper.fetch_add( 1 , std::memory_order_release );
}
double Class::GetAverageUpper( )
{
auto count = m_countUpper.load( std::memory_order_acquire );
auto acc = m_accUpper.load( std::memory_order_relaxed );
return acc/(double)count;
}
假设下部和上部更新总是一起发布,并且没有并发更新。
由于此最后一个字段的发布获取,函数GetAverageLower()
可以保证在m_countLower
发布的更新之前发布任何m_accLower
更新,但可能会更多。双重案例发生在GetAverageUpper()
。
这种思路是否正确?是否保证较低版本实际上总是下限和上限版本,即平均值的上限?
如果这些方法确实按照我的预期进行,那么实际平均值将在关闭的时间间隔内:[GetAverageLower() , GetAverageUpper()]
答案 0 :(得分:2)
在我回答这个问题之前,我觉得有必要陈述一些事情:
只需使用(宽松)原子,就可以保证一个线程会看到其他线程中发生的原子变化。内存重新排序与可见性无关。它是关于防止编译器和CPU加扰代码行。
现在我们确定了这一点,如果您在同一个帖子中调用GetAverageUpper
然后到UpdateUpper
,则会出现问题。内联后,合并的代码将如下所示:
auto count = m_countUpper.load( std::memory_order_acquire );
auto acc = m_accUpper.load( std::memory_order_relaxed );
auto inlinedValue = acc/(double)count;
m_accUpper.fetch_add( delta , std::memory_order_relaxed );
m_countUpper.fetch_add( 1 , std::memory_order_release );
现在,编译器/ CPU无法重新排序acquire
之前的任何行以及release
之后的任何代码行,但中间的两个relaxed
怎么样?他们可以重新订购:
auto count = m_countUpper.load( std::memory_order_acquire );
m_accUpper.fetch_add( delta , std::memory_order_relaxed );
auto acc = m_accUpper.load( std::memory_order_relaxed );
auto inlinedValue = acc/(double)count;
m_countUpper.fetch_add( 1 , std::memory_order_release );
这当然会破坏你的代码逻辑。