【问题标题】:Why is InterLocked is slower than lock?为什么 InterLocked 比 lock 慢?
【发布时间】:2016-05-10 16:05:38
【问题描述】:

出于好奇,我编写了一个程序来测试 InterLocked 与 .Net 中的锁定的性能。 事实证明,InterLocked 版本比 Locked 版本慢得多,如果我在这里遗漏了一些细节,请有人指出。 根据我的理解,Interlocked 的性能应该比 lock 好得多。

public class TestB
    {
        private static readonly object _objLocker = new object();
        private long _shared;
        public void IncrLocked()
        {
            lock (_objLocker)
            {
                _shared++;
            }
        }
        public void IncrInterLocked()
        {
            Interlocked.Increment(ref _shared);
        }
        public long GetValue()
        {
            return _shared;
        }
    }
    class TestsCopy
    {
        private static TestB _testB = new TestB();
        static void Main(string[] args)
        {
            int numofthreads = 100;
            TestInterLocked(numofthreads);
            TestLocked(numofthreads);
            Console.ReadLine();
        }
        private static void TestInterLocked(int numofthreads)
        {
            Thread[] threads = new Thread[numofthreads];
            for (int i = 0; i < numofthreads; i++)
            {
                Thread t = new Thread(StartTestInterLocked);
                threads[i] = t;
                t.Start();
            }
            Stopwatch sw = new Stopwatch();
            sw.Start();
            for (int i = 0; i < threads.Length; i++)
            {
                threads[i].Join();
            }
            sw.Stop();
            Console.WriteLine($"Interlocked finished in : {sw.ElapsedMilliseconds}, value = {_testB.GetValue()}");
        }

        private static void TestLocked(int numofthreads)
        {
            Thread[] threads = new Thread[numofthreads];
            for (int i = 0; i < numofthreads; i++)
            {
                Thread t = new Thread(StartTestLocked);
                threads[i] = t;
                t.Start();
            }
            Stopwatch sw = new Stopwatch();
            sw.Start();
            for (int i = 0; i < threads.Length; i++)
            {
                threads[i].Join();
            }
            sw.Stop();
            Console.WriteLine($"Locked finished in : {sw.ElapsedMilliseconds}, value = {_testB.GetValue()}");
        }

        private static void StartTestInterLocked()
        {
            int counter = 10000000;
            for (int i = 0; i < counter; i++)
            {
                _testB.IncrInterLocked();
            }
        }
        private static void StartTestLocked()
        {
            int counter = 10000000;
            for (int i = 0; i < counter; i++)
            {
                _testB.IncrLocked();
            }
        }

程序的输出是……

Interlocked finished in : 76909 ms, value = 1000000000
Locked finished in : 44215 ms, value = 2000000000

【问题讨论】:

  • 您只是在测试这里有大量并发访问的情况。为了更公平的测试,您还应该在没有并发访问或正常数量的情况下测试哪个更快。
  • 我认为你的测量有缺陷。在启动 StopWatch 之前启动所有线程。许多(如果不是大多数)线程将在测量开始之前完成。

标签: c# multithreading


【解决方案1】:

由于 Jakob Olsen 提到的原因,您的测试存在缺陷。此外,您的测试还包括调用类中方法的开销(对于lock,显然调用Interlocked.Increment() 是无法避免的。

您应该启动所有线程并安排它们在您启动秒表后开始工作。你可以通过让他们等待ManualResetEvent来做到这一点。

我已经像这样重写了你的测试代码:

using System;
using System.Diagnostics;
using System.Threading;

namespace Demo
{
    static class Program
    {
        private static readonly object _objLocker = new object();
        private static long _shared;
        private static ManualResetEvent _signal = new ManualResetEvent(false);

        static void Main(string[] args)
        {
            int numofthreads = 100;
            TestInterLocked(numofthreads);
            TestLocked(numofthreads);
            Console.ReadLine();
        }

        private static void TestInterLocked(int numofthreads)
        {
            Thread[] threads = new Thread[numofthreads];
            for (int i = 0; i < numofthreads; i++)
            {
                Thread t = new Thread(StartTestInterLocked);
                threads[i] = t;
                t.Start();
            }
            Thread.Sleep(5000); // Make sure threads have had time to start.
            Stopwatch sw = new Stopwatch();
            sw.Start();
            _shared = 0;
            _signal.Set();

            for (int i = 0; i < threads.Length; i++)
            {
                threads[i].Join();
            }

            sw.Stop();
            _signal.Reset();
            Console.WriteLine($"Interlocked finished in : {sw.ElapsedMilliseconds}, value = {_shared}");
        }

        private static void TestLocked(int numofthreads)
        {
            Thread[] threads = new Thread[numofthreads];
            for (int i = 0; i < numofthreads; i++)
            {
                Thread t = new Thread(StartTestLocked);
                threads[i] = t;
                t.Start();
            }
            Thread.Sleep(5000); // Make sure threads have had time to start.
            Stopwatch sw = new Stopwatch();
            sw.Start();
            _shared = 0;
            _signal.Set();

            for (int i = 0; i < threads.Length; i++)
            {
                threads[i].Join();
            }

            sw.Stop();
            _signal.Reset();
            Console.WriteLine($"Locked finished in : {sw.ElapsedMilliseconds}, value = {_shared}");
        }

        private static void StartTestInterLocked()
        {
            _signal.WaitOne();
            int counter = 10000000;
            for (int i = 0; i < counter; i++)
            {
                Interlocked.Increment(ref _shared);
            }
        }

        private static void StartTestLocked()
        {
            _signal.WaitOne();
            int counter = 10000000;
            for (int i = 0; i < counter; i++)
            {
                lock (_objLocker)
                {
                    _shared++;
                }
            }
        }
    }
}

现在结果是(来自 RELEASE 版本):

Interlocked finished in : 11339, value = 1000000000
Locked finished in : 30546, value = 1000000000

如您所见,Interlocked 明显更快。

【讨论】:

  • 在我的机器上,输出已完成互锁:139614,值 = 1000000000 已锁定完成:30432,值 = 1000000000
  • 我缺少一些优化,为什么我的机器上的 Interlocked 这么慢?
  • @Bovi_Khurja 你肯定在调试器之外运行发布版本?你有 32 位还是 64 位操作系统?您是否将可执行文件构建为 x64/AnyCPU?
  • 我在调试器之外运行。 64 位操作系统,带 AnyCPU 的 exe
  • @Bovi_Khurja 很奇怪。我无法重现您所看到的。如果您使用int 而不是long,会发生什么?
猜你喜欢
  • 2011-01-25
  • 1970-01-01
  • 2022-08-15
  • 2011-03-23
  • 2019-12-18
  • 2015-10-04
  • 2012-09-16
  • 2016-10-06
  • 2020-11-27
相关资源
最近更新 更多