假设有两个函数需要更新并返回一些被测属性的平均值:
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()
时看到,对于{{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']
是否需要原子?
答案 0 :(得分:0)
您对获取/释放语义如何工作的描述是正确的; 它们用于在存储/发布之前和加载/获取之后的内存操作之间创建一个线程间发生在之间的关系... 这基于运行时关系,并且如果原子加载/获取看到由存储/发布设置的值,则仅定义。
您的代码的第一个问题是它无法满足此运行时要求。
未检查m_count
的值,因此订购保证不适用;因此,您可以在所有操作中使用memory_order_relaxed
。
但仅凭这一点并不能解决问题;当您阅读m_accumulatedValue
时,其值可能会再次被Update()
调用(m_accumulatedValue
因此必须是原子的)。
此外,正如评论部分所指出的那样,由于原子操作之间没有原子性,GetAverage()
可能在Update()
完成之前被调用并返回不正确的值。
您需要的是Update()
和GetAverage()
之间的严格排序,最好的方法是使用std::mutex
。然后原子变量可以是常规整数(如果没有在其他地方使用)。