C# Spinlock的使用

945 阅读1分钟

为建立中文知识库加块砖        ——中科大胡不归

MSDN定义

Spinlock 自旋锁可用于叶级锁,在这种情况 Monitor 下,通过使用、大小或由于垃圾回收压力而隐含的对象分配的成本非常高。 旋转锁定有助于避免阻塞;但是,如果你预计会有大量的阻塞,则可能由于旋转过多而无法使用自旋锁。 当锁的粒度较大且数量较大时,旋转可能非常有利 (例如,链接列表中的每个节点的锁) ,以及锁保持时间始终极短。 通常,在持有自旋锁时,应避免使用以下任何操作:

  • 堵塞
  • 调用自身可能会阻止的任何内容,
  • 同时保留多个自旋锁,
  • (接口和虚方法) 进行动态调度的调用,
  • 对任何代码进行静态调度调用,而不是任何代码,或
  • 分配内存。

示例代码

"Hello World!".PrintGreen();

var spin = new SpinLock(false);
var obj = new object();
long sum1 = 0;
long sum2 = 0;
long sum3 = 0;

// Parallel.for和Parallel.foreach是线程不安全的
var start = DateTime.Now;
Parallel.For(0, 10000000, i =>
{
    sum1 += 1;
});
$"耗时:{DateTime.Now.Subtract(start).TotalMilliseconds}ms".PrintMagenta();

//使用SpinLock
start = DateTime.Now;
Parallel.For(0, 10000000, i =>
{
    var lockToken = false;
    try
    {
        spin.Enter(ref lockToken);
        sum2 += 1;
    }
    finally
    {
        if(lockToken) spin.Exit(false);
    }
});
$"耗时:{DateTime.Now.Subtract(start).TotalMilliseconds}ms".PrintMagenta();

// 使用lock
start = DateTime.Now;
Parallel.For(0, 10000000, i =>
{
    lock (obj)
    {
        sum3 += 1;
    }
});
$"耗时:{DateTime.Now.Subtract(start).TotalMilliseconds}ms".PrintMagenta();

$"sum1的值为:{sum1}".PrintErr();
$"sum1的值为:{sum2}".PrintErr();
$"sum1的值为:{sum3}".PrintErr();

效果演示

demospinlock.png

显然这种情况下,SpinLock比lock效率高很多。

参考文章

  1. c#之task与thread区别及其使用
  2. c#spinLock使用