Antuan Kinnard

in

Mutex and Semaphore WaitHandles: An Overview

I am willing to bet that every developer has been warned about the dangers of developing multi-threaded operations at some point in their career. Let's face it, we have enough to worry about with apps behaving oddly as a result of client machine setup, network configuration, and authorization changes than to let the complexities of threading be added to the list. It's not that it’s such a difficult subject to grasp, but there is plenty to understand in order to create a successful robust implementation. As we all know, the biggest reasons that many will advise careful consideration are well-founded because of its unpredictable nature. Among other issues - like recovering from errors - the complexity of operations happening at the same time is not always very appealing to the developer. You need a way to 'synchronize' threads so that one is not stepping on the other's toes. One attractive way to tackle this problem is the use of waithandles.

The WaitHandle class is located in the System.Threading namespace. Waithandles allow you to manage when, how, and how long a thread will block and wait for access to a piece of functionality. This is done by signaling (or notifying for you Java coders) another thread when the current thread is done. The WaitHandle waits to be signaled when one of the static wait methods - WaitOne(), WaitAny(), or WaitAll() - is called. The WaitOne() method will block all other threads until it is signals them to continue. Along with the timeout parameter, the WaitAll and WaitAny methods take WaitHandle arguments that are used to signal and unblock any waiting threads. Until the waiting threads are signaled (or the timeout is reached), the threads will wait. If you’re developing a process which requires simultaneous access to some shared resource, your implementation should begin with determining how shared the resource should be. When this is done, you can explore a couple more options.

Derived WaitHandles: Mutexes and Semaphores

The Mutex and Semaphore classes are derived from the WaitHandle class. With extra functionality than WaitHandle, they can give you a more scalable solution. Unlike Monitors, these waithandles can be named and accessed across the operating system. Using the OpenExisting() methods, you can access the specified named mutex or semaphore. I like that along with these waithandles, you also get access control security to implement when using it across application domains. You control security by using .NET 2.0's new SetAccessControl() method and the SemphoreSecurity and MutexSecurity classes. Take a look through Microsoft’s documentation on these classes for more information.

As descendants of the WaitHandle class, mutexes and semaphores signal the blocking thread for synchronization using the ReleaseMutex() and Release() methods, respectively. The first thread entering the code ‘locks’ the functionality with a simple call to the WaitOne() method. So what’s the difference between the two, you ask? The answer is that semaphores can count. When the WaitOne() method is called on multiple threads using the same mutex, only the first thread to call the method will continue on while the other threads wait for it to signal that it’s done. Once it’s done, the next one will gain access and the rest will remain blocked, and so on down the line. On the other hand, when the WaitOne() method is called using a semaphore, it will not cause any other thread to be blocked until a zero count is reached. You declare the max count with the call to the constructor. So each time the WaitOne() method is called, the semaphore count decrements by one until 0 is reached. Any other thread blocking at the WaitOne() line will enter the semaphore as the count increases. Depending on your implementation, semaphores can give you even more flexibility and scalability. And what architect wouldn't want that?

Let’s take a look at a couple of examples of how mutexes and semaphores lock:

Mutex Example

using System;
using
System.Threading;

namespace MutexExample {
    public class MutexTest {
        private const int MAX = 5;
        private Mutex _mutex;
        private int _count = 0;

        public void Run() {
            _mutex = new Mutex();           

            for (int i = 0; i < MAX + 2; i++) {
                Thread t = new Thread(new ThreadStart(Start));
                t.Start();
            }
       
}

        private void Start() {
            string name = "Thread " + _count++;

           
_mutex.WaitOne();

           
Console.WriteLine("{0} : Entering mutex", name);
       
    Thread.Sleep(3000);
            Console.WriteLine("{0} : Exiting mutex", name);

           
_mutex.ReleaseMutex();
        }
    }
}

Semaphore Example

using System;
using
System.Threading;

namespace SemaphoreExample {
    class SemaphoreTest {
        private const int MAX = 5;
        private Semaphore _semaphore;
        private int _count = 0;

       
public void Run() {
            _semaphore = new Semaphore(MAX, MAX);

           
for (int i = 0; i < MAX + 2; i++) {
               
Thread t = new Thread(new ThreadStart(Start));
               
t.Start();
           
}
       
}

        private void Start() {
            _semaphore.WaitOne();
            string name = "Thread " + _count++;

           
Console.WriteLine("{0} : Entering semaphore ", name);
           
Thread.Sleep(3000);
           
Console.WriteLine("{0} : Exiting semaphore ", name);

           
_semaphore.Release();
       
}
    }
}

As you can see from these examples, mutexes and semaphores are very simple to use. However, the same may or may not be said when actually putting them to good use, and debugging them. If you’re anything like me, you’re probably already thinking about 2 or 3 instances where these WaitHandles could have helped. If you’re even more like me, you’re probably thinking, “Why would I ever use a mutex over a semaphore.” I would answer, “If you asked that question, you probably wouldn’t.” Most smart developers will adhere to the ever-present goal of creating scalable solutions where the only bounds are that of the programming language. While this may sound facetious, the fact remains that mutexes only allow access to one thread, while semaphores can be much more flexible.

Comments

kMJjmZsZ said:

doors.txt;10;15

# June 1, 2008 3:03 AM
Leave a Comment

(required) 

(required) 

(optional)

(required)