LA.NET [EN]

May 25

When we looked at the kernel objects, we’ve seen that we could use a mutex to achieve critical regions. Whenever we need to build a critical section, we should use the CLR locks instead of relying directly on mutexes. In .NET, any object can be used as a lock when you need to build a critical section. Entering the critical section is achieved through the static Monitor.Enter method. When you’re done, you should call the static Monitor.Exit method.

Here’s a quick example:

var locker = new Object(); //object used for locking
new Thread(() =>
{
    Monitor.Enter(locker);
    try {
        Console.WriteLine("thread 2 acquired lock at: {0}", DateTime.Now);
        Thread.Sleep(1000);
    } finally {
        Console.WriteLine("thread 2 released lock at: {0}", DateTime.Now);
        Monitor.Exit(locker);
    }
}).Start();

Monitor.Enter(locker);
try {
    Console.WriteLine("main thread acquired lock at: {0}", DateTime.Now);
    Thread.Sleep(1000);
}
finally {
    Console.WriteLine("main thread released lock at: {0}", DateTime.Now);               
    Monitor.Exit(locker);
}

As you can see, we’re calling the Enter method to enter the critical region of code and, when we’re done, we’re exiting the region by calling the Exit method. If you run the previous snippet,you’ll see that the one thread will only enter after the other has released the lock. Since the previous pattern (Enter/try/finally/Exit) is so much used,C# supports a shortcut for it through the use of the lock keyword:

var locker = new Object();
new Thread(() => {
    lock(locker){
        Console.WriteLine("thread 2");
    }
}).Start();

lock(locker){
    Console.WriteLine("main thread"); Thread.Sleep(1000);
}

Even though you can “lock” in any object (we’ll look at some code in the next paragraphs), it’s important to keep in mind that you should only use “private” objects. If you leak the “locked” object, you may be letting others interfere with your critical section (it’s no longer yours since anyone can use it for their own locks). This is especially true if you’re building libraries that will be reused by others…

That’s why it’s a bad idea to lock on this, public fields, value types (completely wrong since you’ll be getting “boxed” copies of the value type and this means that each thread will happily enter the critical sections since they’re using different objects for the lock), type objects and appdomain agile objects.Here’s a quick example that shows how things might go wrong when you leak the “locked” object. Suppose you have locker object is exposed from your library and is also used internally for achieving a critical section:

//supose locker and DoSomething are exported
//from your library
static Object locker = new Object();
static void DoSomething() {
    new Thread(() =>
    {
        Thread.Sleep(500); //ensures main thread enters the lock 1st
        lock (locker) {                   
            Console.WriteLine("thread 2");                   
        }
    }).Start();
}
static void Main(string[] args) {
    DoSomething();   
    Console.WriteLine("main thread dealocks the other");
    Monitor.Enter(locker);
    Console.WriteLine("end main");
}

I’ve just locked the program! Yes, I didn’t follow the recommendation of releasing the lock after ending it, but that was the easiest way of showing how your library code (simulated by the DoSomething method and shared locker object) might block if you start sharing the objects you lock on. As a general rule, you should only lock in private objects.

Besides the Enter/Exit method, the Monitor class exposes a third static method which you can use to avoid blocking: I’m talking about the TryEnter method. The class offers several overloads of these method (some let you pass a value for a timeout) and all of them return a boolean. When it returns true, you’ve acquired the lock successfully. If you pass 0 for the timeout (or use the overload which doesn’t let you specify the timeout), the method returns immediately and you should check its return value to see if you’ve acquired the lock.

The other overloads will block by the time you specify or until they acquire the lock. Notice that you cannot use the C# lock sugar for spinning (ie, for calling Monitor.TryEnter) and you’ll have to go with the try/finally approach we’ve seen earlier.

The TryEnter method is especially good when you have other things to do before entering the critical section:

new Thread(() => {
    lock (locker) {
        Console.WriteLine("thread 2 is busy!");
        Thread.Sleep(500);
    }
}).Start();
Thread.Sleep(100);
while (!Monitor.TryEnter(locker, 100)) {
    Console.WriteLine("Doing something else");
    Thread.Sleep(100);
}
try {
    Console.WriteLine( "entered critical section" );
}
finally{
    Console.WriteLine("exited critical section");
    Monitor.Exit(locker);
}

If you run the previous code, you’ll see that it will spin writing “Doing something else” until it enters the critical section. And that’s it for today. Keep tuned for more on CLR locks.

3 comments so far

  1. cialisx
    5:14 pm - 8-21-2009

    Cialis x cheap
    cialis x

  2. Fostar
    3:26 pm - 3-2-2010

    Hey guys,

    Not sure if this is the right section to post this in as it”s sort of off topic.

    I found a leaked copy of Iron Man 2 online at this site: http://550s.info/iron-man-2

    It”s not supposed to be out for another few months but this must”ve got leaked from the studio or something because it”s a good quality copy.

    Anyway hope you guys enjoy it, if anyone else knows of a better copy than this one out there then PM me and let me know.