c#多线程之SpinLock

概述

SpinLock 结构是低级互斥同步基元,在等待获取锁时旋转。 在多核计算机上,如果应缩短等待时间且争用最少,那么 SpinLock 的性能优于其他种类的锁。 不过,建议仅在通过分析确定 System.Threading.Monitor 方法或 Interlocked 方法显著降低程序性能时,才使用 SpinLock。

即使尚未获取锁,SpinLock 也可能会生成线程的时间片。 这样做是为了避免线程优先级反转,并让垃圾回收器能够取得进展。 使用 SpinLock 时,请确保没有线程可以将锁保留比较久的时间,也没有线程可以在保留锁时受阻止。

由于 SpinLock 是值类型,因此如果希望两个副本引用的是同一个锁,必须通过引用显式传递它。


如何使用 SpinLock

在此示例中,关键部分执行的工作量最少,因而非常适合执行 SpinLock。 与标准锁相比,增加一点工作量即可提升 SpinLock 的性能。 但是,超过某个点时 SpinLock 将比标准锁开销更大。 可以使用分析工具中的并发分析功能,查看哪种类型的锁可以在程序中提供更好的性能。

class SpinLockDemo2
{
    const int N = 100000;
    static Queue<Data> _queue = new Queue<Data>();
    static object _lock = new Object();
    static SpinLock _spinlock = new SpinLock();

    class Data
    {
        public string Name { get; set; }
        public double Number { get; set; }
    }
    static void Main(string[] args)
    {

        // First use a standard lock for comparison purposes.
        UseLock();
        _queue.Clear();
        UseSpinLock();

        Console.WriteLine("Press a key");
        Console.ReadKey();
    }

    private static void UpdateWithSpinLock(Data d, int i)
    {
        bool lockTaken = false;
        try
        {
            _spinlock.Enter(ref lockTaken);
            _queue.Enqueue( d );
        }
        finally
        {
            if (lockTaken) _spinlock.Exit(false);
        }
    }

    private static void UseSpinLock()
    {

          Stopwatch sw = Stopwatch.StartNew();

          Parallel.Invoke(
                  () => {
                      for (int i = 0; i < N; i++)
                      {
                          UpdateWithSpinLock(new Data() { Name = i.ToString(), Number = i }, i);
                      }
                  },
                  () => {
                      for (int i = 0; i < N; i++)
                      {
                          UpdateWithSpinLock(new Data() { Name = i.ToString(), Number = i }, i);
                      }
                  }
              );
          sw.Stop();
          Console.WriteLine("elapsed ms with spinlock: {0}", sw.ElapsedMilliseconds);
    }

    static void UpdateWithLock(Data d, int i)
    {
        lock (_lock)
        {
            _queue.Enqueue(d);
        }
    }

    private static void UseLock()
    {
        Stopwatch sw = Stopwatch.StartNew();

        Parallel.Invoke(
                () => {
                    for (int i = 0; i < N; i++)
                    {
                        UpdateWithLock(new Data() { Name = i.ToString(), Number = i }, i);
                    }
                },
                () => {
                    for (int i = 0; i < N; i++)
                    {
                        UpdateWithLock(new Data() { Name = i.ToString(), Number = i }, i);
                    }
                }
            );
        sw.Stop();
        Console.WriteLine("elapsed ms with lock: {0}", sw.ElapsedMilliseconds);
    }
}

猜你喜欢

转载自blog.csdn.net/a_codecat/article/details/128449924