如何在C#中实现自己的TaS-Lock?

时间:2016-11-29 17:17:03

标签: c# .net multithreading

因此,对于赋值,我们需要选择使用C#-Lock或使用自实现的TaS-Lock。我读过的有关TaS-Locks的内容是它使用1个原子步骤来读取和写入值。有人建议我们在C#中使用Interlocked类。

到目前为止,这是我所得到的,但似乎导致答案不一致:

public interface Lock
{
    void Lock();
    void Unlock();
}

public class C_Sharp_Lock : Lock
{
    readonly Object myLock = new object();

    public void Lock()
    {
        Monitor.Enter(myLock);
    }

    public void Unlock()
    {
        Monitor.Exit(myLock);
    }
}

public class Tas_Lock : Lock
{
    int L = 0;

    public void Lock()
    {
        while (0 == Interlocked.Exchange(ref L, 1)) { };
    }

    public void Unlock()
    {
        Interlocked.Exchange(ref L, 0);
    }
}

有谁知道我在这里做错了什么?

编辑:作为对凯文的回复:

我已将其更改为以下内容:

public class Tas_Lock : Lock
{
    int L = 0;

    public void Lock()
    {
        while (0 == Interlocked.CompareExchange(ref L, 1, 0)) { };
    }

    public void Unlock()
    {
        Interlocked.Exchange(ref L, 0);
    }
}

然而,这仍会导致不一致的结果。

编辑#2:对C#lock的更改:

public class C_Sharp_Lock : Lock
{
    readonly Object myLock = new object();
    bool lockTaken = false;

    public void Lock()
    {
        Monitor.Enter(myLock, ref lockTaken);
    }

    public void Unlock()
    {
        if (lockTaken)
            Monitor.Exit(myLock);
    }
}

1 个答案:

答案 0 :(得分:5)

你误解了Interlocked.CompareExchange的工作方式。如果它之前等于提供的比较,并返回之前的值,则会自动交换值。

简而言之,Interlocked.CompareExchange(ref L, 1, 0)将:

  • 检查L是否等于0
  • 如果L等于0,则将L设置为1并返回先前的值(0)
  • 如果L不等于0(因此等于1),则它将返回先前的值(1)

从那里,你应该做的是循环直到 Interlocked.CompareExchange返回0(这意味着获得了锁)。在您的代码中,当 Interlocked.CompareExchange返回0时,您正在等待

固定代码:

public class Tas_Lock
{
    int L = 0;

    public void Lock()
    {
        while (0 != Interlocked.CompareExchange(ref L, 1, 0)) { }
    }

    public void Unlock()
    {
        Interlocked.Exchange(ref L, 0);
    }
}

有两点需要注意:

  • Interlocked.Exchange中的Unlock可以被更快的Volatile.Write取代(甚至可以说是一个简单的写法)
  • 如果不是作业,你可以使用内置的课程SpinLock,它已经以优化的方式完成所有这些工作