gpt4 book ai didi

c# - 使计算线程安全的标准方法是什么?

转载 作者:太空宇宙 更新时间:2023-11-03 18:49:17 24 4
gpt4 key购买 nike

最初看似简单解决方案的问题已被证明是一个非常有趣的挑战。

我有一个类维护一个内部固定大小、线程安全的集合(通过对所有插入和删除操作使用 lock)并通过其属性提供各种统计值。

一个例子:

public double StandardDeviation {
get {
return Math.Sqrt((Sum2 - ((Sum * Sum) / Count)) / Count);
}
}

现在,我已经彻底测试了这个计算,通过集合运行 10,000 个值并检查每次更新的标准偏差。它工作正常...在单线程场景中。

然而,在我们的开发和生产环境的多线程上下文中出现了一个问题。似乎这个数字以某种方式有时返回NaN,然后迅速变回实数。当然,这一定是由于传递给 Math.Sqrt 的负值所致。我只能想象,当计算进行到一半时,计算中使用的值之一由单独的线程更新时会发生这种情况。

我可以先缓存值:

int C = this.Count;
double S = this.Sum;
double S2 = this.Sum2;

return Math.Sqrt((S2 - (S * S) / C) / C);

但是 Sum2 可能仍会更新,例如,在设置 S = this.Sum 之后,再次影响计算。

我可以在代码中更新这些值的所有点周围放置一个:

protected void ItemAdded(double item) {
// ...

lock (this.CalculationLock) {
this.Sum += item;
this.Sum2 += (item * item);
}
}

然后,如果我在计算 StandardDeviation锁定同一对象,我认为最终会解决问题。它没有。该值仍然以 NaN 的形式出现,但并不频繁。

坦率地说,即使上述解决方案已经 奏效,它也非常困惑并且对我来说似乎不太好管理。 是否有标准和/或更直接的方法来实现计算值的线程安全,例如这样?


编辑:原来这里有一个问题示例,起初似乎只有一个可能的解释,但毕竟问题完全出在其他问题上。

我一直一丝不苟地以各种可能的方式实现线程安全,而尽可能不牺牲巨大的性能——锁定对共享值的读取和写入(例如,SumCount),在本地缓存值,并使用相同的锁对象来修改集合和更新共享值……老实说,这一切看起来都有些矫枉过正。

没有任何效果;那个邪恶的 NaN 不断弹出。所以我决定在 StandardDeviation 返回 NaN 时将集合中的所有值打印到控制台...

我立即注意到,当集合中的所有值都相同时,似乎总是会发生这种情况

这是官方的:我被浮点运算烧坏了。 (所有的值都相同,所以 StandardDeviation 中的被除数——即被取平方根的数——被计算为某个极小的负数。)

最佳答案

I could put a lock around all points in the code where these values are updated:

protected void ItemAdded(double item) {
// ...

lock (this.CalculationLock) {
this.Sum += item;
this.Sum2 += (item * item);
}
}

Then if I lock on this same object when calculating StandardDeviation, I thought that would, finally, fix the problem. It didn't. The value is still coming in as NaN on a fleeting, infrequent basis.

正是您应该为正确性做的事情。如果这对您不起作用,我建议您要么错过了更新场景 - 或者您有其他问题(例如 Sum 或 Sum2 偶尔为 NaN 或由于某些 而出现意外值其他竞争条件)。

关于c# - 使计算线程安全的标准方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1544142/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com