Выпуск Получить семантику для вычисления среднего

Скажем, есть две функции для обновления и возврата среднего значения некоторого измеряемого свойства:

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_count в GetAverage() запрещает переупорядочение груза m_accumulatedValue перед этим и в то же время гарантирует, что любое изменение m_accumulatedValue в исполнении Update() виден потоком, вызывающим GetAverage() после изменения m_count также видно, для магазина, выполненного на m_cout от Update() имеет порядок релиза.

Правильно ли то, что я только что сказал?

Есть ли GetAverage() (с указанной гарантией не одновременности обращений к Update()) всегда возвращаете правильный ответ? Или может быть способ вернуть вычисленное среднее с некоторыми значениями, «более обновленными», чем другие?

Есть ли m_accumulatedValue нужно быть атомным вообще?

1

Решение

Ваше описание того, как работает семантика получения / выпуска, является правильным;
они используются для создания потока случается, перед тем связь между операциями с памятью до сохранения / выпуска и после загрузки / получения …
Это основано на отношениях во время выполнения и является только определяется, если атомная загрузка / получение видит значение, которое было установлено в хранилище / выпуске.

Первая проблема с вашим кодом заключается в том, что он не соответствует требованиям времени выполнения.
Значение m_count не проверяется и, следовательно, гарантии заказа не применяются; поэтому вы могли бы также использовать memory_order_relaxed на все операции.

Но это само по себе не решает проблему; когда ты читаешь m_accumulatedValueего значение могло снова измениться при следующем вызове Update() (m_accumulatedValue поэтому должен быть атомным).
Кроме того, как указано в разделе комментариев, так как между атомарными операциями нет атомарности, GetAverage() может быть вызван раньше Update() закончен и возвращает неправильное значение.

То, что вам нужно, это строгий порядок между Update() а также GetAverage() и лучший способ сделать это с std::mutex, Тогда атомарные переменные могут быть обычными целыми числами (если не используются где-либо еще).

0

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]