C# 如何以线程安全的方式递增(向)十进制数?

C# 如何以线程安全的方式递增(向)十进制数?,c#,thread-safety,C#,Thread Safety,我有一个decimal变量,可以同时从多个线程访问该变量互锁类函数根本不支持小数,因此我剩下的唯一方法就是使用lock(){}。这似乎太过分了 是否有其他方法可以以线程安全的方式向十进制变量添加值?使用锁并不过分。这是必需的 像System.Decimal这样的结构类型从来不是原子的,它也不适合本机cpu字大小。这就是为什么Interlocted也没有重载。否。十进制的内部表示太复杂,无法在CPU级别用原子指令进行修改(这是Interlocted大部分时间所做的,也是您感兴趣的) 当CPU不能自

我有一个
decimal
变量,可以同时从多个线程访问该变量<代码>互锁类函数根本不支持小数,因此我剩下的唯一方法就是使用
lock(){}
。这似乎太过分了


是否有其他方法可以以线程安全的方式向
十进制
变量添加值?

使用锁并不过分。这是必需的


像System.Decimal这样的结构类型从来不是原子的,它也不适合本机cpu字大小。这就是为什么Interlocted也没有重载。

否。
十进制
的内部表示太复杂,无法在CPU级别用原子指令进行修改(这是
Interlocted
大部分时间所做的,也是您感兴趣的)


当CPU不能自动处理某些数量时,手动锁定是唯一的选择。您可以选择同步原语(例如,
lock
与mutex),但仅此而已。

您仍然可以使用
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
79228162514264337593543950335
,而
Int64.MaxValue
9223372036854775807
。所以非常大的数字是不合适的。保留4位小数,Int64溢出前的最大数字为
9223372036854775807/10000=922337203685477


我这样使用它,因为这里的数字永远不会超过100000000,而且我确信使用
联锁
这种方式在并行中速度更快。对于循环,然后使用
锁或互斥锁。

如果您不介意将总数作为对象包装
十进制
,您可以使用这种方法:

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)位的


@马尔克,放松点。平行的。因为我用过,只有完成了才可以。然后,完成
/10000
操作。只要totalAmount以线程安全的方式求和,一切都很好。并非所有代码都是“关键部分”,只有求和才是关键部分<代码>totalDecimalAmount
在所有线程完成后进行评估。明白了,我真的应该更仔细地阅读代码示例注释。没有大括号、省略号或其他伪代码指示符,我只是通读了一遍。与锁定方法相比,您是否检查了此解决方案的性能?虽然此分配是原子的,但每次分配时都必须分配一个新对象,这是一个性能问题。此外,比较实例还需要覆盖
Equals()
以比较它们的
class A
{
    decimal Value{get;set;}
}

var x=new A(){Value=10};
var y=new A(){Value=20};

x=y;//atomic