Wednesday 12 August 2015

Thread Synchronization with SpinLock and How to Implement it in C# & VB

Ever face a situation where you need to access a value concurrently and prevent race condition from happening. For your application that deals with multiple threads and every threads needs to have exclusive right to change the value, there are a few kinds of lock that can be used. For more details on the list of available locks, you can refer to the following link https://msdn.microsoft.com/en-us/library/ms228964(v=vs.110).aspx

For now we will look into SpinLock. What Spinlock does is that if thread [A] has attain the lock, other threads will just wait for the thread [A] to release the lock. While waiting for the thread [A] to release the lock, the spinlock will just spin in the loop until the lock is being release by the thread [A].

Due to this, SpinLock is only suitable if the lock is being held in a very short duration. Acquiring the lock for a long duration will reduce the performance of other applications since more CPU cycles is being used. Also, it is a waste of CPU time to held the lock for a long duration, since it basically just do nothing other than loop until the lock is available.

So, before we look into how to use SpinLock, make sure your project is targeting .NET Framework version 4 and above.

Implementing SpinLock is pretty easy and straight forward. Firstly, you need to have an instance of SpinLock to get started.

[C#]
SpinLock spinLock = new SpinLock();

[VB]
Dim spinLock As SpinLock = New SpinLock()


The isLocked variable, which is a boolean variable, is to define whether the thread owns the lock. isLocked variable needs to be declared as a local variable, so that the value don't get overwrite by other threads.

[C#]
bool isLocked = false;

[VB]
Dim isLocked As Boolean = False


spinlock.Enter is to acquire the lock and wait for the lock to be released if is owned by other thread.

[C#]
spinLock.Enter(ref isLocked);

[VB]
spinLock.Enter(isLocked)


spinlock.Exit is to release the lock and to enable other threads to acquire the lock.

[C#]
spinLock.Exit();

[VB]
spinLock.Exit()


So let's take an example where you would like to have a multiple threads accessing a value from a variable and at the same time need to ensure that race condition did not happen. To prove that race condition did not happen with SpinLock, I have created a simple sample in a console to demonstrate this. Assuming the following method is to increase the value of an integer. To prevent other threads from accessing the variable _valueWithLock, the method is being implemented it with SpinLock.

[C#]
private static void ChangeValueWithSpinLock()
{
    var isLocked = false;
    try
    {
        _spinLock.Enter(ref isLocked);
        _valueWithLock++;
    }
    finally
    {
        if (isLocked)
        {
            _spinLock.Exit();
        }
    }
}

[VB]
Private Sub ChangeValueWithSpinLock()
    Dim isLocked As Boolean = False

    Try
        _spinLock.Enter(isLocked)
        _valueWithLock += 1
    Finally
        If isLocked Then
            _spinLock.Exit()
        End If
    End Try
End Sub

To simulate multiple threads accessing the method, a Parallel class is being used. It will loop and call the methods 1000000 times.

[C#]
Parallel.For(0, 1000000, (i) => {
    ChangeValue();
});

[VB]
Parallel.For(0, 1000000, New Action(Of Integer)(Sub()
                                                    ChangeValue()
                                                End Sub))

By running the codes, you will notice that the value will increase to 1000000. Which means no race condition occurred. 

The sample project can be obtained here. https://1drv.ms/u/s!Aj2AA7hoIWHmgmKoweIKoaf5z9hf




No comments:

Post a Comment