为什么 Interlocked.CompareExchange只支持引用类型?
免责声明:我的帖子显然总是很冗长。如果您碰巧知道标题问题的答案,请随意回答,而无需阅读下面的扩展讨论。
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
显然具有 where T : class
约束,并且我不明白为什么 。我的意思是,也许这是因为 CompareExchange
已经有接受 Int32
、Int64
、Double 的重载
等;但这似乎并不是一个很好的理由。例如,就我而言,能够使用 Aggregate
Disclaimer: My posts are apparently always verbose. If you happen to know the answer to the title question, feel free to just answer it without reading my extended discussion below.
The System.Threading.Interlocked
class provides some very useful methods to assist in writing thread-safe code. One of the more complex methods is CompareExchange
, which can be used for computing a running total that may be updated from multiple threads.
Since the use of CompareExchange
is a bit tricky, I thought it a rather common-sense idea to provide some helper methods for it:
// 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; });
}
Now, perhaps I am just guilty of being generic-happy (I will admit, this is often true); but it does feel silly to me to restrict the functionality provided by the above methods to double
values only (or, more accurately, for me to have to write overloaded versions of the above methods for every type I want to support). Why can't I do this?
// 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)
)
);
}
I can't do this because Interlocked.CompareExchange<T>
apparently has a where T : class
constraint, and I don't understand why. I mean, maybe it's because there are already overloads for CompareExchange
that accept Int32
, Int64
, Double
, etc.; but that hardly seems a good rationale. In my case, for example, it would be quite handy to be able to use the Aggregate<T>
method to perform a wide range of atomic calculations.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
Interlocked.CompareExchange 旨在使用处理器直接提供的本机原子指令来实现。在内部使用类似的锁是没有意义的(它是为无锁场景设计的)。
提供原子比较交换指令的处理器自然支持它作为小型“寄存器大小”操作(例如,Intel x64 处理器上最大的比较交换指令是作用于 128 位值的
cmpxchg16b
)。任意值类型可能比该值更大,并且使用单个指令可能无法对其进行比较交换。比较-交换引用类型很容易。无论内存中的总大小如何,您都将比较和复制已知大小的小指针。对于像
Int32
和Double
这样的基本类型也是如此——它们都很小。Interlocked.CompareExchange
is meant to be implemented with native atomic instructions provided directly by the processor. It's pointless to have something like that use alock
internally (it's designed for lock-free scenarios).Processors that provide atomic compare exchange instruction naturally support it as small, "register-sized" operations (e.g. the largest compare-exchange instruction on an Intel x64 processor is
cmpxchg16b
that works on 128 bit values).An arbitrary value type can be potentially bigger than that and compare-exchanging it may not be possible with a single instruction. Compare-exchanging a reference type is easy. Regardless of its total size in memory, you'll be comparing and copying a small pointer of a known size. This is also true for primitive types like
Int32
andDouble
—all of them are small.因为该重载专门用于比较和交换引用。它不使用 Equals() 方法执行相等性检查。由于值类型永远不会与您要比较的值具有引用相等性,我的猜测是它们将 T 限制为类以防止误用。
Because that overload is specifically intended to compare and exchange a reference. It does not perform an equality check using the Equals() method. Since a value type would never have reference equality with the value you're comparing it against, my guess is that they constrained T to class in order to prevent misuse.
我怀疑 Interlocked.CompareExchange只是在幕后执行原子指针交换。
尝试使用值类型来执行此操作可能不会给出您期望的结果。
当然,您可以在使用值类型之前将它们装箱到
对象
中。I would suspect that
Interlocked.CompareExchange<T>
just performs an atomic pointer-swap under the hood.Trying to do that with a value type would likely not give the results you expect.
You could, of course, box value types up in an
object
before using them.