发布获取语义以计算平均值

时间:2017-02-02 16:27:03

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

假设有两个函数需要更新并返回一些被测属性的平均值:

void Class::Update( int delta )
{
    m_accumulatedValue += delta;
    ++ m_count;
}

double Class::GetAverage( )
{
    return m_accumulatedValue/(double)m_count;
}

现在,假设需要将它们更改为在具有线程池的多线程环境中运行,其中可以请求任何线程执行其中一个线程 - 也就是说,执行其中每个线程的线程可以是不同的线程时间:

std::atomic< int > m_accumulatedValue;
std::atomic< int > m_count;

// ...

void Class::Update( int delta )
{
    m_accumulatedValue.fetch_add( delta , std::memory_order_relaxed );
    m_count.fetch_add( 1 , std::memory_order_release );
}

double Class::GetAverage( )
{
    auto count = m_count.load( std::memory_order_acquire );
    auto acc = m_accumulatedValue.load( std::memory_order_relaxed );

    return acc/(double)count;
}

我正在尝试理解获取和释放内存排序。

假设Update()的同一对象没有并发调用,但可能是Update()GetAverage()的同一对象的并发调用。

对于我所读到的内容,m_countGetAverage()的获取负载禁止重新排序m_accumulatedValue之前的负载和同时 >保证m_accumulatedValue执行的Update()GetAverage()执行的任何更改都可以通过调用m_count的线程在m_cout更改Update()时看到,对于{{1}上执行的商店} GetAverage()有一个发布顺序。

我刚刚说的是对吗?

Update()(具有对m_accumulatedValue的调用的非并发性的所述保证)是否始终返回正确的答案?或者有一种方法可以返回计算出的平均值,其中一些值比另一些更“更新”?

BaseAdminClass(admin.ModelAdmin): fields = ['some_field', 'many_to_many_field'] filter_horizontal = ['many_to_many_field'] ChildAdminClassOne(admin.ModelAdmin): def get_fields(self, request, obj=None): fields = super(ChildAdminClass, self).get_fields(request, obj) return fields + ['another_field'] # Something like get_filter_horizontal()? filter_horizontal = BaseAdminClass.filter_horizontal + ['another_field'] 是否需要原子?

1 个答案:

答案 0 :(得分:0)

您对获取/释放语义如何工作的描述是正确的; 它们用于在存储/发布之前和加载/获取之后的内存操作之间创建一个线程间发生在之间的关系... 这基于运行时关系,并且如果原子加载/获取看到由存储/发布设置的值,则仅定义

您的代码的第一个问题是它无法满足此运行时要求。 未检查m_count的值,因此订购保证不适用;因此,您可以在所有操作中使用memory_order_relaxed

但仅凭这一点并不能解决问题;当您阅读m_accumulatedValue时,其值可能会再次被Update()调用(m_accumulatedValue因此必须是原子的)。 此外,正如评论部分所指出的那样,由于原子操作之间没有原子性,GetAverage()可能在Update()完成之前被调用并返回不正确的值。

您需要的是Update()GetAverage()之间的严格排序,最好的方法是使用std::mutex。然后原子变量可以是常规整数(如果没有在其他地方使用)。