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




No comments:

Post a Comment