It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center




已关闭8年。




我已经在互联网上阅读了很多文档和文章以及帖子。
几乎每个人都 promise ,对于较短的代码段,SpinLock更快,但是我进行了测试,在我看来,简单的Monitor.Enter比SpinLock.Enter更快(针对.NET 4.5编译测试)
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;
using System.Linq;
using System.Globalization;
using System.ComponentModel;
using System.Threading;
using System.Net.Sockets;
using System.Net;

class Program
{
    static int _loopsCount = 1000000;
    static int _threadsCount = -1;

    static ProcessPriorityClass _processPriority = ProcessPriorityClass.RealTime;
    static ThreadPriority _threadPriority = ThreadPriority.Highest;

    static long _testingVar = 0;


    static void Main(string[] args)
    {
        _threadsCount = Environment.ProcessorCount;

        Console.WriteLine("Cores/processors count: {0}", Environment.ProcessorCount);

        Process.GetCurrentProcess().PriorityClass = _processPriority;

        TimeSpan tsInterlocked = ExecuteInterlocked();
        TimeSpan tsSpinLock = ExecuteSpinLock();
        TimeSpan tsMonitor = ExecuteMonitor();

        Console.WriteLine("Test with interlocked: {0} ms\r\nTest with SpinLock: {1} ms\r\nTest with Monitor: {2} ms",
            tsInterlocked.TotalMilliseconds,
            tsSpinLock.TotalMilliseconds,
            tsMonitor.TotalMilliseconds);

        Console.ReadLine();
    }

    static TimeSpan ExecuteInterlocked()
    {
        _testingVar = 0;

        ManualResetEvent _startEvent = new ManualResetEvent(false);
        CountdownEvent _endCountdown = new CountdownEvent(_threadsCount);

        Thread[] threads = new Thread[_threadsCount];

        for (int i = 0; i < threads.Length; i++)
        {
            threads[i] = new Thread(() =>
                {
                    _startEvent.WaitOne();

                    for (int j = 0; j < _loopsCount; j++)
                    {
                        Interlocked.Increment(ref _testingVar);
                    }

                    _endCountdown.Signal();
                });

            threads[i].Priority = _threadPriority;
            threads[i].Start();
        }

        Stopwatch sw = Stopwatch.StartNew();

        _startEvent.Set();
        _endCountdown.Wait();

        return sw.Elapsed;
    }

    static SpinLock _spinLock = new SpinLock();

    static TimeSpan ExecuteSpinLock()
    {
        _testingVar = 0;

        ManualResetEvent _startEvent = new ManualResetEvent(false);
        CountdownEvent _endCountdown = new CountdownEvent(_threadsCount);

        Thread[] threads = new Thread[_threadsCount];

        for (int i = 0; i < threads.Length; i++)
        {
            threads[i] = new Thread(() =>
            {
                _startEvent.WaitOne();

                bool lockTaken;

                for (int j = 0; j < _loopsCount; j++)
                {
                    lockTaken = false;

                    try
                    {
                        _spinLock.Enter(ref lockTaken);

                        _testingVar++;
                    }
                    finally
                    {
                        if (lockTaken)
                        {
                            _spinLock.Exit();
                        }
                    }
                }

                _endCountdown.Signal();
            });

            threads[i].Priority = _threadPriority;
            threads[i].Start();
        }

        Stopwatch sw = Stopwatch.StartNew();

        _startEvent.Set();
        _endCountdown.Wait();

        return sw.Elapsed;
    }

    static object _locker = new object();

    static TimeSpan ExecuteMonitor()
    {
        _testingVar = 0;

        ManualResetEvent _startEvent = new ManualResetEvent(false);
        CountdownEvent _endCountdown = new CountdownEvent(_threadsCount);

        Thread[] threads = new Thread[_threadsCount];

        for (int i = 0; i < threads.Length; i++)
        {
            threads[i] = new Thread(() =>
            {
                _startEvent.WaitOne();

                bool lockTaken;

                for (int j = 0; j < _loopsCount; j++)
                {
                    lockTaken = false;

                    try
                    {
                        Monitor.Enter(_locker, ref lockTaken);

                        _testingVar++;
                    }
                    finally
                    {
                        if (lockTaken)
                        {
                            Monitor.Exit(_locker);
                        }
                    }
                }

                _endCountdown.Signal();
            });

            threads[i].Priority = _threadPriority;
            threads[i].Start();
        }

        Stopwatch sw = Stopwatch.StartNew();

        _startEvent.Set();
        _endCountdown.Wait();

        return sw.Elapsed;
    }
}

在具有24个2.5 GHz内核的服务器上,使用x64编译的此应用程序产生以下结果:
Cores/processors count: 24
Test with interlocked: 1373.0829 ms
Test with SpinLock: 10894.6283 ms
Test with Monitor: 1171.1591 ms

最佳答案

您只是不测试SpinLock可以改善线程处理的方案。自旋锁背后的核心思想是线程上下文切换是非常昂贵的操作,成本在2000到10,000 cpu周期之间。而且,如果线程有可能通过等待一个位(旋转)来获取锁,那么可以通过避免线程上下文切换来偿还等待的额外循环。

因此,基本要求是锁定必须保持很短的时间,在您的情况下确实如此。并且有合理的几率可以获取该锁。在您的情况下,这是不正确的,锁是受到至少24个线程的竞争。所有旋转和燃烧的核心都没有机会获得锁。

在此测试中,Monitor会最好地工作,因为它会将等待获取锁的线程排队。它们将被挂起,直到其中之一有机会获得该锁为止(在释放锁时从等待队列中释放该锁)。给他们一个公平的机会转弯,从而最大程度地提高他们同时完成的几率。互锁。增量也不错,但不能提供公平保证。

您必须衡量一下,很难判断Spinlock是否是正确的方法。并发分析器是正确的工具。

09-08 07:45