免责声明:我的帖子显然总是很冗长.如果您碰巧知道标题问题的答案,请随时回答它而不阅读下面的扩展讨论.
本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, Funcaggregator) { 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(ref T value, Func aggregator) where T : IEquatable { T initial, aggregated; do { initial = value; aggregated = aggregator(initial); } while ( !initial.Equals( Interlocked.CompareExchange (ref value, aggregated, initial) ) ); }
我不能这样做,因为Interlocked.CompareExchange
显然有一个where T : class
约束,我不明白为什么.我的意思是,也许是因为目前已经有重载CompareExchange
接受Int32
,Int64
,Double
等; 但这似乎不是一个很好的理由.例如,在我的情况下,能够使用该Aggregate
方法执行各种原子计算将非常方便.
Interlocked.CompareExchange
意图用由处理器直接提供的本机原子指令来实现.这样的东西使用lock
内部(它是为无锁方案设计的)是没有意义的.
提供原子比较交换指令的处理器自然支持它作为小的"寄存器大小"操作(例如,英特尔x64处理器上最大的比较交换指令cmpxchg16b
适用于128位值).
任意值类型可能比这更大,并且使用单个指令进行比较 - 交换它可能是不可能的.比较 - 交换参考类型很容易.无论内存中的总大小如何,您都将比较和复制已知大小的小指针.这也是对基本类型真正喜欢Int32
和Double
-所有都是小.