DCSIMG

 Subscribe in a reader

What Gives? Lock With Timeouts - Guy kolbis
Tuesday, January 27, 2009 1:40 PM kolbis

What Gives? Lock With Timeouts

The lock keyword in C# is used to synchronize threads and to allow a simple way to write thread-safe code. It has a really simple usage:

lock (syncObject)
{
    //do something
}

Behind the scenes the compiler will convert the code into:

Monitor.Enter(syncObject);
try
{
    //do something 
}
finally
{
    Monitor.Exit(syncObject);
}

Having said that, the lock keyword can be dangerous because it can lead to deadlocks. In this post I will supply a small solution to this problem:  "Lock with timeouts".

Consider this code:

if (!Monitor.TryEnter(syncObject, TimeSpan.FromSeconds(5))
{
    throw new ApplicationException("Timeout waiting for lock");
}
try
{
    //do something 
}
finally
{
    Monitor.Exit(syncObject);
}

As you can see with this code I use Monitor.TryEnter and not the Monitor.Enter. That means that if within the given time-span I will not acquire a lock, I will through an exception and will prevent a deadlock possibility. If the compiler could generate this code from the lock keyword we could have use it for "Timed Lock". However, this is not the case.

This solution will insure that you will never have a deadlock that is caused by the lock keyword.

The TimedLock struct

The solution is based on Ian Griffiths post. To sum things up for you, here is what you need to do in order to implement TimedLock:

using System;
using System.Threading;

#if DEBUG
public class TimedLock : IDisposable
#else
public struct TimedLock : IDisposable
#endif
{
    public static TimedLock Lock (object o)
    {
        return Lock (o, TimeSpan.FromSeconds (10));
    }

    public static TimedLock Lock (object o, TimeSpan timeout)
    {
        TimedLock tl = new TimedLock (o);
        if (!Monitor.TryEnter (o, timeout))
        {
#if DEBUG
            System.GC.SuppressFinalize(tl);
#endif
            throw new LockTimeoutException ();
        }

        return tl;
    }

    private TimedLock (object o)
    {
        target = o;
    }
    private object target;

    public void Dispose ()
    {
        Monitor.Exit (target);

        // It's a bad error if someone forgets to call Dispose,
        // so in Debug builds, we put a finalizer in to detect
        // the error. If Dispose is called, we suppress the
        // finalizer.
#if DEBUG
        GC.SuppressFinalize(this);
#endif
    }

#if DEBUG
    ~TimedLock()
    {
        // If this finalizer runs, someone somewhere failed to
        // call Dispose, which means we've failed to leave
        // a monitor!
        System.Diagnostics.Debug.Fail("Undisposed lock");
    }
#endif

}
public class LockTimeoutException : Exception
{
    public LockTimeoutException () : base("Timeout waiting for lock")
    {
    }
}

In order to use it:

using (TimedLock.Lock(obj))
{
}

You can read the Ian's post here.

תגים:, ,

תוכן התגובה

# Why Concurrency Is Hard (Or: TimedLock Can Get You in Trouble)

I’ve just noticed a post by Guy Kolbis discussing a possible solution for deadlocks – ensuring that all

Tuesday, January 27, 2009 5:11 PM

# re: What Gives? Lock With Timeouts

Sebastian כתב/ה

If you look at the generated machine code, you'll notice that:

lock (syncObject)

{

   //do something

}

and:

Monitor.Enter(syncObject);

try

{

   //do something

}

finally

{

   Monitor.Exit(syncObject);

}

will _not_ generate the same code! The explicit Monitor Enter/Exit code uses more instructions, if I remember correctly also an extra register and in performance tests will execute slower. At least that was my experience with Microsofts 2.0 CLR... That killed the performance of our application when we tried to do something like this.

Thursday, January 29, 2009 6:42 PM

שלח תגובה

(שדה חובה) 
(שדה חובה) 
(אופציונלי)
(שדה חובה) 

Enter the numbers above: