Showing posts with label Thread Synchronization. Show all posts
Showing posts with label Thread Synchronization. Show all posts

Friday, 13 November 2015

Thread Synchronization with Semaphore and how to Implement it in C# & VB

In the previous couple of posts, I have posted about aquiring exclusive lock with SpinLock http://jaryl-lan.blogspot.com/2015/08/exclusive-lock-with-spinlock-c-vb.html or with Mutex http://jaryl-lan.blogspot.com/2015/08/thread-synchronization-with-mutex.html. So for today, I'm going to post about something different yet it can still provide exclusive right to a thread in a multi-threaded environment, which is Semaphore.

Semaphore is used to control the number of concurrent threads to access a particular section of the code / resources. With the ability to control the number of concurrent threads, It can do more than just acquiring exclusive lock by limiting to 1 thread. I will demonstrate on how to use Semaphore to control the concurrent threads with an example. The example below is by no means the best practice, it is just to show how Semaphore works.

Imagine that you need to process more than 1 file at the same time with FileSystemWatcher. By default, the event only process 1 file at a time. So to process multiple file at the same time and control the threads, we will use Task.Run to simulate fire-and-forget and Semaphore to control the threads.

As usual, you need an instance of Semaphore. The following constructor accept 2 parameters. The first parameter define the number of free / available requests can be accepted. The second parameter define the maximum number of concurrent threads. In this case, we have 3 available requests and can accept only up to 3 concurrent threads.

[C#]
Semaphore semaphore = new Semaphore(3, 3);

[VB]
Dim semaphore As Semaphore = New Semaphore(3, 3)


In your FileSystemWatcher's created event, call WaitOne() to occupy 1 available request. If the request is not available, it will wait until a new request is available.

[C#]
semaphore.WaitOne();

[VB]
semaphore.WaitOne()


We will use Task.Run to execute the method in another thread. In that method, call Release() to release the request. Allowing another thread to acquire the available request.

[C#]
semaphore.Release();

[VB]
semaphore.Release()



If you play around with the sample code, you will notice that the thread id is different from when it call WaitOne() and call Release(). This is due to the thread that execute Task.Run spawn a new thread and since it did not call await or .Wait() for the Task.Run, it end up leaving the Task.Run without waiting for its completion and exit the method. To re-explain this in steps, 
  1. Thread A call WaitOne() and execute Task.Run.
  2. Task.Run spawn a new thread B to further execute the method specified in Task.Run. 
  3. Thread A leaves without waiting thread B completes its execution and exit the method. (fire-and-forget)
  4. Thread B executing the method and call Release().




Sunday, 30 August 2015

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

Previously, I have posted about acquiring exclusive lock with SpinLock http://jaryl-lan.blogspot.com/2015/08/thread-synchronization-with-spinlock.html. Today, we will look into how to implement and use the Mutex. [I would like to give credits to Serena Yeoh for introduce this to me.]

The purpose for SpinLock and Mutex are quite similar, which is for applications that deal with multiple threads and every threads requires to have exclusive right to perform a task. But unlike SpinLock, Mutex are not limited to current process and can share across different processes. In Mutex, threads that are waiting for acquiring the lock are waiting for signal from the thread that is holding the lock.

To use the Mutex, An instance of a Mutex is required.

[C#]
Mutex mutex = new Mutex();

[VB]
Dim mutex As Mutex = New Mutex()

The code above can only be used in the current process. To share the Mutex across other processes, you need to give the Mutex a name. Here are a few things to take note when instantiating an instance of Named Mutex. For more details, https://msdn.microsoft.com/en-us/library/system.threading.mutex.aspx & https://msdn.microsoft.com/en-us/library/windows/desktop/ms682411(v=vs.85).aspx.
  • The name given to the Mutex are case sensitive, so make sure to take note of the name's casing when you want to share the same Named Mutex across your applications.
  • There are two types of prefix, which is "Global\" and "Local\". By default, if you do not specify any prefix for the Named Mutex, it will be "Local\".
  • If you need to share your Mutex across different Environment (For Example: Web Application, Windows Application), you need to include the prefix "Global\" in the name of the Mutex.
  • Other than prefix, the remaining character for the Named Mutex cannot contain backslash (\).
Other than that, you need to define the security access for the Mutex. This is to allow other processes that are launched by different user to share the same Mutex.

What the following code does is to try and get the existing Named Mutex. If is doesn't exist, a new Named Mutex will be created. Regarding the parameter MutexRights, specifying synchronize is to allow the thread to wait for the Named Mutex's lock. As for Modify, it is to allow the thread to release the lock of the Named Mutex.

The SecurityIdentifier is to define what kind of users can use the Named Mutex, in this case, specifying WorldSid is to allow all users (also known as Everyone) to use the Named Mutex.

[C#]
if (!Mutex.TryOpenExisting(MUTEX_NAME, MutexRights.Synchronize | MutexRights.Modify, out mutex))
{
    bool createdNew;
    MutexAccessRule mutexAccessRule = new MutexAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null), MutexRights.Synchronize | MutexRights.Modify, AccessControlType.Allow);

    MutexSecurity mutexSecurity = new MutexSecurity();
    mutexSecurity.AddAccessRule(mutexAccessRule);

    mutex = new Mutex(false, MUTEX_NAME, out createdNew, mutexSecurity);
}

[VB]
If mutex.TryOpenExisting(MUTEX_NAMEMutexRights.Synchronize Or MutexRights.Modify, mutex) Then
    Dim createdNew As Boolean
    Dim mutexAccessRule As MutexAccessRule = New MutexAccessRule(New SecurityIdentifier(WellKnownSidType.WorldSid, Nothing), MutexRights.Synchronize Or MutexRights.Modify, AccessControlType.Allow)

    Dim mutexSecurity As MutexSecurity = New MutexSecurity()
    mutexSecurity.AddAccessRule(mutexAccessRule)

    mutex = New Mutex(False, MUTEX_NAME, createdNew, mutexSecurity)
End If

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

[C#]
mutex.WaitOne();

[VB]
_mutex.WaitOne()

mutex.ReleaseMutex is to release the lock and to enable other threads to acquire the lock.

[C#]
mutex.ReleaseMutex();

[VB]
_mutex.ReleaseMutex()

By using the same scenario as specified in my previous blog post about SpinLock. Race Condition will not happen if used together with Mutex. By running the sample code for SpinLock and Mutex, you will notice that SpinLock are much faster compared to Mutex.

The test is run with the following specification:
Operating System: Windows Server 2012 R2 64 Bit
Processor: Intel Core i7-4800MQ Processor
RAM: 16GB, Dual Channel, DDR3

Parallel Loop for 1,000,000 times.
Elapsed time for SpinLock: 126.08 ms ~ 129.57 ms
Elapsed time for Mutex: 2774.81 ms ~ 2868.85 ms

Hold it. Don't jump to a conclusion that SpinLock is better than Mutex. In this test, what i'm doing is just changing a single variable value. This means that each threads acquire the lock and release it in a very short duration, in which it can be ideal for SpinLock. But if the thread takes a very long time to complete a task, you will see the performance of the application starts to degrade. SpinLock will also affect other processes' performance while waiting for the lock to be release from the thread, since it uses CPU cycle while spinning in the loop.

You can get the sample code about Mutex from the following link. https://onedrive.live.com/redir?resid=E6612168B803803D!334&authkey=!ACaO5bWfJ1DdEJs&ithint=file%2czip

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