简体   繁体   English

为什么Interlocked.CompareExchange <T> 只支持参考类型?

[英]Why does Interlocked.CompareExchange<T> only support reference types?

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. System.Threading.Interlocked类提供了一些非常有用的方法来帮助编写线程安全的代码。 One of the more complex methods is CompareExchange , which can be used for computing a running total that may be updated from multiple threads. 其中一个更复杂的方法是CompareExchange ,它可用于计算可从多个线程更新的运行总计。

Since the use of CompareExchange is a bit tricky, I thought it a rather common-sense idea to provide some helper methods for it: 由于使用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; });
}

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). 但是觉得将上述方法提供的功能限制为仅double值(或者更确切地说,对于我必须为我想支持的每种类型编写上述方法的重载版本)确实感到愚蠢。 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 . 我不能这样做,因为Interlocked.CompareExchange<T>显然有一个where T : class约束, 我不明白为什么 I mean, maybe it's because there are already overloads for CompareExchange that accept Int32 , Int64 , Double , etc.; 也就是说, 也许是因为CompareExchange已经有重载接受Int32Int64Double等; 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. 例如,在我的情况下,能够使用Aggregate<T>方法执行各种原子计算将非常方便。

Interlocked.CompareExchange is meant to be implemented with native atomic instructions provided directly by the processor. Interlocked.CompareExchange旨在使用处理器直接提供的本机原子指令来实现。 It's pointless to have something like that use a lock internally (it's designed for lock-free scenarios). 这样的东西在内部使用lock (它是为无锁方案设计的)是没有意义的。

Processors that provide atomic compare exchange instruction naturally support it as small, "register-sized" operations (eg the largest compare-exchange instruction on an Intel x64 processor is cmpxchg16b that works on 128 bit values). 提供原子比较交换指令的处理器自然支持它作为小的“寄存器大小”操作(例如,英特尔x64处理器上最大的比较交换指令是cmpxchg16b ,它适用于128位值)。

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 and Double —all of them are small. 这也是原始的类型,如真正Int32Double -所有都是小。

Because that overload is specifically intended to compare and exchange a reference. 因为该重载特别用于比较和交换引用。 It does not perform an equality check using the Equals() method. 它不使用Equals()方法执行相等性检查。 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. 由于值类型永远不会与您比较它的值具有引用相等性,我的猜测是它们将T限制为类以防止滥用。

I would suspect that Interlocked.CompareExchange<T> just performs an atomic pointer-swap under the hood. 我怀疑Interlocked.CompareExchange<T>只是在引擎盖下执行原子指针交换。

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. 当然,在使用它们之前,您可以在object中输入框值类型。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM