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

标签 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( )

据我所知,GetAverage() 中的 m_count 的获取负载禁止对其之前的 m_accumulatedValue 的负载重新排序并且同时保证Update()m_accumulatedValue 执行的任何更改对调用GetAverage()< 的线程可见 一旦对 m_count 的更改也被看到,因为 Update()m_cout 上执行的存储有一个发布顺序。

我刚才说的对吗?

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

m_accumulatedValue 是否需要原子性?

最佳答案

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

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

但这并不能解决问题;当您读取 m_accumulatedValue 时,它的值可能已通过对 Update() 的另一次调用再次更改(因此 m_accumulatedValue 必须是原子的)。 此外,正如评论部分指出的那样,由于原子操作之间没有原子性,因此可能会在 Update() 完成之前调用 GetAverage() 并返回错误值(value)。

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

关于c++ - 发布获取语义以计算平均值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42007362/

相关文章:

c++ - 是否可以将基于范围的 for 循环与迭代器范围一起使用?

c++ - 适用于调试但不适用于发布

线程可以在接收到条件信号时唤醒但不能同时获得互斥锁

concurrency - 如何在 rust 中执行线程安全 IO 和缓存到文件?

java - 并发集合的contain-like方法没用吗?

c++ - C++继承更改成员

c++ - 在另一个系统上构建时禁用特定的 C++ 行和#includes

java - 并发求和列表,由另一个线程填充

android - Android 上如何确定设备内存不足

c# - C# 中是否有类似 ConcurrentQueue 的数据结构,它允许我等待一个空队列,直到添加一个项目?