7

免责声明:我的帖子显然总是很冗长。如果您碰巧知道标题问题的答案,请随意回答,而无需阅读下面的扩展讨论。


该类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该 accept Int32Int64Double等已经有重载;但这似乎不是一个好的理由。例如,在我的情况下,能够使用该Aggregate<T>方法执行广泛的原子计算将非常方便。

4

3 回答 3

12

Interlocked.CompareExchange旨在使用处理器直接提供的本机原子指令来实现。在内部使用类似的东西是没有意义的lock(它是为无锁场景设计的)。

提供原子比较交换指令的处理器自然支持它作为小型的“寄存器大小”操作(例如,英特尔 x64 处理器上最大的比较交换指令cmpxchg16b适用于 128 位值)。

任意值类型可能比这更大,并且通过单个指令可能无法进行比较交换。比较交换引用类型很容易。无论它在内存中的总大小如何,您都将比较和复制一个已知大小的小指针。Int32对于像和这样的原始类型也是如此Double——它们都很小。

于 2010-02-11T01:11:46.140 回答
1

因为该重载专门用于比较和交换参考。它不使用 Equals() 方法执行相等检查。由于值类型永远不会与您与之进行比较的值具有引用相等性,因此我的猜测是它们将 T 限制为类以防止误用。

于 2010-02-11T01:13:20.387 回答
-1

我怀疑这Interlocked.CompareExchange<T>只是在引擎盖下执行原子指针交换。

尝试使用值类型执行此操作可能不会给出您期望的结果。

当然,您可以object在使用它们之前将值类型框起来。

于 2010-02-11T01:09:47.340 回答