释放获取语义以计算平均值的下限和上限

时间:2017-02-02 17:46:04

标签: c++ multithreading concurrency atomic memory-barriers

基于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()]

1 个答案:

答案 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 );

这当然会破坏你的代码逻辑。