The Darkside

Shedding light on things and stuff

 
  Home :: Contact :: Syndication  :: Login
  87 Posts :: 0 Stories :: 56 Comments :: 2 Trackbacks

Ads

 

Donate via PayPal...

...if you feel the site helped.

Archives

Post Categories

Open Source Projects

Other Blogs

This is a short explanatory article on lock, the Monitor object, the Mutex object and finally the Semaphore object.

To start of, the lock key word is syntactic sugar for the code you would write (mostly) using Monitor. You would use lock to stop a thread from accessing a critical section of code that another thread is currently in. If you try to lock a critical section that another thread is in, the current thread will be blocked until the thread in the critical section exits.

An example of needing to use a lock would be a class that kept count of doing things (or stuff). Your code calls the method IncrementCounterA, but you notice that when many threads access it at the same time, it isn't counting correctly. If you make use of the lock keyword, you can block access to the critical section, in the case the "_CounterA++" section, and ensure the the number of things (or stuff) is always correct. Here is an example of using lock.

private static readonly object _SyncLock = new object();
 
private int _CounterA;
public void IncrementCounterA()
{
    lock (_SyncLock)
    {
        _CounterA++;
    }
}

You could also make use of the static Monitor class and the code above, rewritten using Monitor, would like like the following code:

private static readonly object _SyncLock = new object();
 
private int _CounterB;
public void IncrementCounterB()
{
 
    try
    {
        Monitor.Enter(_SyncLock);
        _CounterB++;
    }
    finally
    {
        Monitor.Exit(_SyncLock);
    }
}

Now, let's say you had a library that logged error to a file. Originally, your code looked like this:

public void LogErrorToFile (string error)
{
    File.AppendAllText("errors.log", error); 
}

You start using it in more and more applications, but notice every now and then that you get an access denied error. You change it to make use of "lock"...

private static object _SyncLock = new object();
 
public void LogErrorToFile(string error)
{
    lock (_SyncLock)
    {
        File.AppendAllText("errors.log", error);
    }
}

... and the error rate drops a little, but some still occur. Here is when you'd make use of the Mutex class.

private Mutex _Mutex = new Mutex(false, "DarksideErrorLog");
 
public void LogErrorToFile (string error)
{
    try
    {
        _Mutex.WaitOne();
        File.AppendAllText("errors.log", error);
    }
    finally
    {
        _Mutex.ReleaseMutex();
    }
}

A mutex allows inter-process synchronisation to occur. If you instantiate a mutex with a name (as in the code above), the mutex becomes system-wide. This is really useful if you're sharing the same library between many different applications and need to block access to a critical section of code that is accessing resources that can't be shared.

Finally, the Semaphore class. Let's say you have a method that is really CPU intensive, and also makes use of resources that you need to control access to (using Mutexes :)). You've also determined that a maximum of five calls to the method is about all your machine can hanlde without making it unresponsive. Your best solution here is to make use of the Semaphore class which allows you to limit a certain number of threads' access to a resource.

private Semaphore _Semaphore = new Semaphore(0, 5, "DarksideIntensiveStuff");
 
public void DoIntensiveStuff()
{
    try
    {
        _Semaphore.WaitOne();
        //Simulate long-running/cpu-intensive process...
        Thread.Sleep(5000); 
    }
    finally
    {
        _Semaphore.Release(1);
    }
}

Again, like with the mutex, creating a semaphore with a name makes it system-wide, so multiple applications calling your method will all be subject to the same semaphore.

posted on Friday, August 08, 2008 2:09 PM
Comments have been closed on this topic.