Release Получите семантику для вычисления нижней и верхней границы среднего

На основе предыдущий вопрос, Мне было интересно, будет ли следующий код работать для вычисления нижних и верхних границ для среднего значения свойства, измеряемого с использованием атомики:

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

Решение

Прежде чем я отвечу на этот вопрос, я чувствую необходимость заявить кое-что:
Просто используя (расслабленную) атомарность, вы гарантируете, что один поток увидит изменение в атомарном, которое произошло в других потоках. переупорядочение памяти не о видимости. Речь идет о предотвращении компиляции и процессора от шифрования строк кода.

Теперь, когда мы это установили, возникает проблема, если вы позвоните 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 );

Теперь компилятор / процессор не может переупорядочивать любые строки, которые предшествуют 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 );

Что, конечно, нарушает вашу логику кода.

2

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

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

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