-->

当应该与不应该我用这个C#实用工具类,以控制通过互锁螺纹(When should & shouldn

2019-09-17 02:31发布

我想了解这背后类是如何写的逻辑,当我应该和不应该使用它。 任何有识之士将不胜感激

internal struct SpinLock
{
    private volatile int lockHeld;

    private readonly static int processorCount;

    public bool IsHeld
    {
        get
        {
            return this.lockHeld != 0;
        }
    }

    static SpinLock()
    {
        SpinLock.processorCount = Environment.ProcessorCount;
    }

    public void Enter()
    {
        if (Interlocked.CompareExchange(ref this.lockHeld, 1, 0) != 0)
        {
            this.EnterSpin();
        }
    }

    private void EnterSpin()
    {
        int num = 0;
        while (this.lockHeld != null || Interlocked.CompareExchange(ref this.lockHeld, 1, 0) != 0)
        {
            if (num >= 20 || SpinLock.processorCount <= 1)
            {
                if (num >= 25)
                {
                    Thread.Sleep(1);
                }
                else
                {
                    Thread.Sleep(0);
                }
            }
            else
            {
                Thread.SpinWait(100);
            }
            num++;
        }
    }

    public void Exit()
    {
        this.lockHeld = 0;
    }
}

更新:我发现在我的源代码样例用法......这说明了如何使用上述对象,虽然我不明白“为什么”

    internal class FastReaderWriterLock
    {
        private SpinLock myLock;

        private uint numReadWaiters;

        private uint numWriteWaiters;

        private int owners;

        private EventWaitHandle readEvent;

        private EventWaitHandle writeEvent;

        public FastReaderWriterLock()
        {
        }

        public void AcquireReaderLock(int millisecondsTimeout)
        {
            this.myLock.Enter();
            while (this.owners < 0 || this.numWriteWaiters != 0)
            {
                if (this.readEvent != null)
                {
                    this.WaitOnEvent(this.readEvent, ref this.numReadWaiters, millisecondsTimeout);
                }
                else
                {
                    this.LazyCreateEvent(ref this.readEvent, false);
                }
            }
            FastReaderWriterLock fastReaderWriterLock = this;
            fastReaderWriterLock.owners = fastReaderWriterLock.owners + 1;
            this.myLock.Exit();
        }

private void WaitOnEvent(EventWaitHandle waitEvent, ref uint numWaiters, int millisecondsTimeout)
{
    waitEvent.Reset();
    uint& numPointer = numWaiters;
    bool flag = false;
    this.myLock.Exit();
    try
    {
        if (waitEvent.WaitOne(millisecondsTimeout, false))
        {
            flag = true;
        }
        else
        {
            throw new TimeoutException("ReaderWriterLock timeout expired");
        }
    }
    finally
    {
        this.myLock.Enter();
        uint& numPointer1 = numWaiters;
        if (!flag)
        {
            this.myLock.Exit();
        }
    }
}
    }

Answer 1:

自旋锁一般都是锁的形式继续等待线程苏醒(遍地自行车在紧张的检查条件环-认为“ 妈妈我们到了没?”),而不是依赖较重,速度较慢,内核模式的信号。 它们通常用于在预期等待时间很短的情况下,他们胜过创建和等待一个OS手柄传统锁的开销。 他们承担比传统的锁更多的CPU成本的,所以比在很短的等待时间,传统的锁(如多监视器类或各种WaitHandle的实现)是首选。

这个简短的等待时间概念是体现在你上面的代码:

waitEvent.Reset();
// All that we are doing here is setting some variables.  
// It has to be atomic, but it's going to be *really* fast
uint& numPointer = numWaiters;
bool flag = false;
// And we are done.  No need for an OS wait handle for 2 lines of code.
this.myLock.Exit();

有一个非常好的自旋锁内置到BCL ,但它仅在4.0以上,所以如果你是在.NET framework的或代码的旧版本这是从旧版本的迁移工作,可能是有人写自己的实现。

要回答你的问题:如果你是在.NET 4.0或更高版本编写新的代码,您应该使用内置的自旋锁。 有关3.5或旧的代码,特别是如果你正在扩展Nesper,我认为这是执行时间考验的和适当的。 只有当你知道的时候线程可能等待它非常的小,如上面的例子中使用自旋锁。

编辑:看起来你实现从Nesper-的艾斯波CEP库的.NET端口传来:

https://svn.codehaus.org/esper/esper/tagsnet/release_1.12.0_beta_1/trunk/NEsper/compat/SpinLock.cs

https://svn.codehaus.org/esper/esper/tagsnet/release_1.12.0_beta_1/trunk/NEsper/compat/FastReaderWriterLock.cs

我可以证实,Nesper存在很长时间了.NET框架4之前,这样解释了一个家纺自旋锁的需要。



Answer 2:

看来,原作者想更快的版本ReaderWriterLock 。 这老班是痛苦的缓慢。 我自己的测试(我做了很久以前)表示RWL了〜8X一个普通的旧的开销lockReaderWriterLockSlim改进的东西相当多的(尽管它仍然有2倍〜开销相比, lock )。 在这一点上,我会说沟自定义代码,只需使用较新的ReaderWriterLockSlim类。

但是,对于什么是值得让我解释一些的自定义SpinLock码。

  • Interlocked.CompareExchange是.NET的版本的CAS操作。 这是最根本同步原语。 你可以从字面上建立从这个单一的操作,包括自己的自定义一切Monitor般的类,读写器锁等。显然,在这里用来创建一个自旋锁。
  • Thread.Sleep(0)产生,以与任何处理器上相同或更高的优先级的任何线程。
  • Thread.Sleep(1)得到在任何处理器的任何线程。
  • Thread.SpinWait使线程进入一个紧密循环的迭代指定数量。

虽然它不是在代码中使用您发布没有用于创建自旋锁(或其他低锁定策略),另一种有用的机制。

  • Thread.Yield产生到同一处理器上的任何线程。

Microsoft使用都在各自的高并发的同步机制和集合这些调用。 如果你反编译SpinLockSpinWaitManualResetEventSlim ,等你会看到一个相当复杂的歌曲和舞蹈的事情与这些电话......远远超过您发布的代码更加复杂。

同样,沟自定义代码,只需使用ReaderWriterLockSlim而不是定制的FastReaderWriterLock类。


顺便说一句, this.lockHeld != null ,因为要产生一个编译器警告lockHeld是值类型。



文章来源: When should & shouldn't I use this C# utility class to control threads via Interlocked