免责声明:我的帖子显然总是很冗长。如果您碰巧知道标题问题的答案,请在不阅读下面的扩展讨论的情况下随时回答。
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>
方法执行各种原子计算将非常方便。
答案 0 :(得分:12)
Interlocked.CompareExchange
意味着使用处理器直接提供的本机原子指令来实现。这样的东西在内部使用lock
(它是为无锁方案设计的)是没有意义的。
提供原子比较交换指令的处理器自然支持它作为小的“寄存器大小”操作(例如,英特尔x64处理器上的最大比较交换指令是cmpxchg16b
,其工作在128位值)。
任意值类型可能比这更大,并且使用单个指令进行比较 - 交换它可能是不可能的。比较 - 交换参考类型很容易。无论内存中的总大小如何,您都将比较和复制已知大小的小指针。对于Int32
和Double
这样的原始类型也是如此 - 所有这些类型都很小。
答案 1 :(得分:1)
因为该重载特别用于比较和交换引用。它不使用Equals()方法执行相等性检查。由于值类型永远不会与您比较它的值具有引用相等性,我猜他们将T限制为类以防止滥用。
答案 2 :(得分:-1)
我怀疑Interlocked.CompareExchange<T>
只是在引擎盖下执行原子指针交换。
尝试使用值类型执行此操作可能无法提供您期望的结果。
当然,在使用它们之前,您可以在object
中输入框值类型。