免责声明:我的帖子显然总是冗长的。如果您碰巧知道标题问题的答案,请随意回答,无需阅读我下面的扩展讨论。System.Threading.Interlocked
类提供了一些非常有用的方法来帮助编写线程安全代码。一种更复杂的方法是 CompareExchange
,它可用于计算可以从多个线程更新的运行总数。
由于 CompareExchange
的使用有点棘手,我认为为其提供一些辅助方法是一个相当常识性的想法:
// code mangled so as not to require horizontal scrolling
// (on my monitor, anyway)
public static double Aggregate
(ref double value, Func<double, double> aggregator) {
double initial, aggregated;
do {
initial = value;
aggregated = aggregator(initial);
} while (
initial != Interlocked.CompareExchange(ref value, aggregated, initial)
);
return aggregated;
}
public static double Increase(ref double value, double amount) {
return Aggregate(ref value, delegate(double d) { return d + amount; });
}
public static double Decrease(ref double value, double amount) {
return Aggregate(ref value, delegate(double d) { return d - amount; });
}
现在,也许我只是因为喜欢通用而感到内疚(我承认,这通常是真的);但我觉得 将上述方法提供的功能仅限于
double
值确实很愚蠢(或者,更准确地说,我必须为我想要支持的每种类型编写上述方法的重载版本)。为什么我不能这样做?// the code mangling continues...
public static T Aggregate<T>
(ref T value, Func<T, T> aggregator) where T : IEquatable<T> {
T initial, aggregated;
do {
initial = value;
aggregated = aggregator(initial);
} while (
!initial.Equals(
Interlocked.CompareExchange<T>(ref value, aggregated, initial)
)
);
}
我不能这样做,因为
Interlocked.CompareExchange<T>
显然有一个 where T : class
约束,而 我不明白为什么 。我的意思是,也许是因为 CompareExchange
已经有接受 Int32
、 Int64
、 Double
等的重载;但这似乎不是一个很好的理由。例如,在我的情况下,能够使用 Aggregate<T>
方法执行广泛的原子计算将非常方便。 最佳答案
Interlocked.CompareExchange
旨在使用处理器直接提供的 native 原子指令来实现。让类似的东西在内部使用 lock
是没有意义的(它是为无锁场景设计的)。
提供原子比较交换指令的处理器自然支持它作为小的“寄存器大小”操作(例如,Intel x64 处理器上最大的比较交换指令是 cmpxchg16b
,它适用于 128 位值)。
任意值类型可能比它更大,并且可能无法使用单个指令进行比较交换。比较交换引用类型很容易。不管它在内存中的总大小如何,您都将比较和复制一个已知大小的小指针。对于 Int32
和 Double
这样的原始类型也是如此——它们都很小。