我有一个decimal
同时从多个线程访问的变量。Interlocked
类函数根本不支持小数,所以我剩下的唯一方法是使用lock(){}
. 这似乎是一种矫枉过正。
还有其他方法可以以decimal
线程安全的方式向变量添加值吗?
我有一个decimal
同时从多个线程访问的变量。Interlocked
类函数根本不支持小数,所以我剩下的唯一方法是使用lock(){}
. 这似乎是一种矫枉过正。
还有其他方法可以以decimal
线程安全的方式向变量添加值吗?
使用锁并不过分。这是必需的。
像 System.Decimal 这样的结构类型永远不是原子的,它也不适合原生 cpu 字长。这就是为什么 Interlocked 也没有过载的原因。
不可以。 的内部表示decimal
太复杂,无法在 CPU 级别使用原子指令进行修改(这是Interlocked
大多数时间所做的,也是您感兴趣的)。
当 CPU 无法自动处理某些数量时,手动锁定是唯一的选择。您可以选择同步原语(例如lock
与互斥锁),仅此而已。
您仍然可以使用InterLocked
,但是您必须将小数转换为Int64
. 通过转换,您必须决定要保留多少小数位以确保精度。因此,例如,您想保留 4 个小数位,您可以执行以下操作:
//Declare up front accessible from all threads
Int64 totalAmount = 0;
//Inside the thread you do this
var amount = (Int64)(decimalAmount * 10000); //10.000 is to preserve 4 decimal places
Interlocked.Add(ref totalAmount, amount);
//After all threads have finished, go back to decimal type.
var totalDecimalAmount = totalAmount / 10000;
请注意,您将失去精度,具体取决于您要保留多少小数位。并且Decimal.MaxValue
是79,228,162,514,264,337,593,543,950,335
而是。Int64.MaxValue
_ 9,223,372,036,854,775,807
所以非常大的数字不适合。保留 4 位小数,Int64 溢出之前的最大数字是9,223,372,036,854,775,807 / 10000 = 922,337,203,685,477
我以这种方式使用它,因为这里的数字永远不会超过 1,000,000,000,而且我确信Interlocked
在 Parallel.For 循环中使用这种方式比使用lock
或互斥锁更快。
如果您不介意将总数保留为 object-wrapped decimal
,则可以使用以下方法:
private static object myTotal = 0M;
static void InterlockedAddTotal(decimal val) {
object next;
object current;
do {
current = myTotal;
next = val + (decimal)current;
} while (Interlocked.CompareExchange(ref myTotal, next, current) != current);
}
尽管这种方法不使用锁,但它包装decimal
在一个对象中,该对象具有其自身的性能影响。根据情况,使用锁可能更便宜。
使分配原子化的一种方法是创建一个内部具有十进制值的类并将引用分配给该类。类引用的分配是原子的,因为它们是 64 (32) 位
class A
{
decimal Value{get;set;}
}
var x=new A(){Value=10};
var y=new A(){Value=20};
x=y;//atomic