Heijmaaans Heijmaaans - 6 days ago 6
C# Question

How do I implement my own TaS-Lock in C#?

So for an assignment we need to have the option to either use the C#-Lock or use a self-implemented TaS-Lock. What I've read about TaS-Locks is that it uses 1 atomic step to both read and write a value. It was suggested to us that we use the Interlocked class in C# for this.

So far this is what I've got, but it seems to result in inconsistent answers:

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);
}
}


Does anyone know what I'm doing wrong here?

Edit: As a response to Kevin:

I've changed it to the following:

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);
}
}


However this still returns inconsistent results.

Edit #2: Changes to 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);
}
}

Answer

You're misunderstanding the way Interlocked.CompareExchange works. It swaps atomatically a value if it's previously equal to the provided comparand, and returns the previous value.

In short, Interlocked.CompareExchange(ref L, 1, 0) will:

  • Check whether L is equal to 0
  • If L is equal to 0, then it will set L to 1 and return the previous value (0)
  • If L isn't equal to 0 (and therefore is equal to 1), then it will return the previous value (1)

From there, what you should do is loop until Interlocked.CompareExchange returns 0 (which means that the lock was acquired). In your code, you're waiting while Interlocked.CompareExchange returns 0.

The fixed code:

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);
    }
}

Two things to note:

  • The Interlocked.Exchange in Unlock could be replaced by a faster Volatile.Write (or even, though arguable, a simple write)
  • If it wasn't for an assignment, you could use the built-in class SpinLock, which already does all that stuff in an optimized way
Comments