C# Interlocked 类( 二 )

volatile 修饰符,阻止寄存器缓存变量值(关于volatile不在此赘述),然后直接调用了C++底层内部函数 __sync_val_compare_and_swap 实现原子性的比较交换操作,这里直接用的是 CPU 指令进行原子性操作,性能非常高 。
相同机制函数和 Increment 函数机制类似,Interlocked 类下的大部分方法都是通过 CompareExchange 底层函数来操作的,因此这里不再赘述

  • Add 添加值
  • CompareExchange 比较交换
  • Decrement 自减
  • Exchange 交换
  • And 按位与
  • Or 按位或
  • Read 读64位数值
public static long Read(ref long location)Read 这个函数着重提一下
C# Interlocked 类

文章插图
可以看到这个函数没有 32 位(int)类型的重载,为什么要单独为 64 位的 long/ulong 类型单独提供原子性读取操作符呢?
这是因为CPU有 32 位处理器和 64 位处理器,在 64 位处理器上,寄存器一次处理的数据宽度是 64 位,因此在 64 位处理器和 64 位操作系统上运行的程序,可以一次性读取 64 位数值 。
但是在 32 位处理器和 32 位操作系统情况下,long/ulong 这种数值,则要分成两步操作来进行,分别读取 32 位数据后,再合并在一起,那显然就会出现多线程情况下的并发问题 。
因此这里提供了原子性的方法来应对这种情况 。
C# Interlocked 类

文章插图
这里底层同样用了 CompareExchange 操作来保证原子性,参数这里就给了两个0,可以兼容如果原值是 0 则写入 0 ,如果原值非 0 则不写入,返回原值 。
__sync_val_compare_and_swap 函数在写入新值之前, 读出旧值, 当且仅当旧值与存储中的当前值一致时,才把新值写入存储
【关于性能】多线程下实现原子性操作方式有很多种,我们一定会关心在不同场景下,不同方法间的性能问题,那么我们简单来对比下 Interlocked 类提供的方法和 lock 关键字的性能对比
我们同样用线程池调度50个Task(内部可能线程重用),分别执行 200000 次自增运算
public static void IncreamentPerformance(){//lock methodvar locker = new object();var stopwatch = new Stopwatch();stopwatch.Start();var j1 = 0;Task.WaitAll(Enumerable.Range(0, 50).Select(t =>Task.Run(() =>{for (int i = 0; i < 200000; i++){lock (locker){j1++;}}})).ToArray());Console.WriteLine($"Monitor lock,result={j1},elapsed={stopwatch.ElapsedMilliseconds}");stopwatch.Restart();//Increment methodvar j2 = 0;Task.WaitAll(Enumerable.Range(0, 50).Select(t =>Task.Run(() =>{for (int i = 0; i < 200000; i++){Interlocked.Increment(ref j2);}})).ToArray());stopwatch.Stop();Console.WriteLine($"Interlocked.Increment,result={j2},elapsed={stopwatch.ElapsedMilliseconds}");}运算结果
C# Interlocked 类

文章插图
可以看到,采用 Interlocked 类中的自增函数,性能比 lock 方式要好一些
虽然这里看起来性能要好,但是不同的业务场景要针对性思考,采用恰当的编码方式,不要一味追求性能
我们简单分析下造成执行时间差异的原因
我们都知道,使用lock(底层是Monitor类),在上述代码中会阻塞线程执行,保证同一时刻只能有一个线程执行 j1++ 操作,因此能保证操作的原子性,那么在多核CPU下,也只能有一个CPU核心在执行这段逻辑,其他核心都会等待或执行其他事件,线程阻塞后,并不会一直在这里傻等,而是由操作系统调度执行其他任务 。由此带来的代价可能是频繁的线程上下文切换,并且CPU使用率不会太高,我们可以用分析工具来印证下 。

经验总结扩展阅读