tags:

views:

76

answers:

3

I often see code like that which is shown here, ie where an object is allocated and then used as a "lock object".

It seems to me that you could use any object for this, including the event itself as the lock object. Why allocate a new object that does nothing? My understanding is that calling lock() on an object doesn't actually alter the object itself, nor does it actually lock it from being used, it's simply used as a placeholder for multiple lock statements to anchor on.

public class Shape : IDrawingObject, IShape
{
    // Create an event for each interface event
    event EventHandler PreDrawEvent;
    event EventHandler PostDrawEvent;

    object objectLock = new Object();

    // Explicit interface implementation required.
    // Associate IDrawingObject's event with
    // PreDrawEvent
    event EventHandler IDrawingObject.OnDraw
    {
        add
        {
            lock (objectLock)
            {
                PreDrawEvent += value;
            }
        }
        remove
        {
            lock (objectLock)
            {
                PreDrawEvent -= value;
            }
        }
    }
}

So my question is, is this really a good thing to do?

+1  A: 

It is recommended to lock on a private static field because this ensures that multiple threads trying to access the lock in parallel will be blocked. Locking on the instance of the class itself (lock(this)) or some instance field could be problematic because if two threads call the method on two different instances of the object they will be able to simultaneously enter the lock statement.

Darin Dimitrov
Yes, but that's not what I said. I was referring to using the private event member itself as the locking variable (ie. PreDrawEvent). I should also note that the example isn't using a static variable. Besides, locking on this would be problematic if you had more than one lock statement within the class.
Mystere Man
Same thing: locking on `this` or `instance field` could be bad as the instance field will change depending on the instance of the object while a static readonly field will never change.
Darin Dimitrov
I really don't see what the issue is. The whole point is to lock an instance of a variable so that multiple threads cannot change that instance. Why would you want to prevent two different instances from being modified simultaneously?
Mystere Man
In his case there is nothing wrong with using the object instance as the monitor as the critical section us only mutating the instance's own data structures. It would be perfectly safe for two separate thread instances to enter this monitor at the same time. It would not be safe for two threads of execution to enter the monitor of /this/ instance at the same time. So in this case, an instance variable or even the object itself works fine as the monitor.
Jason Coco
In this case it is OK, but imagine for a moment that the method modifies some shared data (like a static variable).
Darin Dimitrov
Right. So why allocation the object? It just seems redundant when you can lock on the event itself. I am assuming here that events are value types, or at worst compiler controlled automatic reference types so that the lock were being placed on the event itself, and not the events being assigned (I honestly am not sure what event types are, but I think this is a safe assumption). I have to wonder how many people copy and paste from the MSDN and incur this kind of redundancy without even realizing it.. or worse.
Mystere Man
+6  A: 

including the event itself

No, you can't do that. An "event" is actually just some accessor methods. Assuming you mean the backing delegate, that would be very bad - delegates are immutable: every time you add/remove a subscriber, you get a different delegate.

Actually, the 4.0 compiler now does this with lock-free code using Interlocked - it may be worth following this approach instead.

In your example, the objectLock ensures that all callers (to that instance) are locking against the same object, which is important - but without the ugliness of locking on this (which is how the C# compiler used to work).

--

Update: your example shows code that is necessary before C# 4.0, access to a field-like-event inside the type talked directly to the field: the normal field-like-event locking was not respected. This was changed in C# 4.0; you can now (in C# 4.0) safely re-write this as:

public class Shape : IDrawingObject, IShape
{
    // Create an event for each interface event
    event EventHandler PreDrawEvent;
    event EventHandler PostDrawEvent;

    event EventHandler IDrawingObject.OnDraw
    {
        add { PreDrawEvent += value; }
        remove { PreDrawEvent -= value; }
    }
}

All the correct behaviour is then followed.

Marc Gravell
So you're saying that if lock(PreDrawEvent) would result in a different object being locked every time the delegate was changed? Ugh.. That doesn't seem very.. intuitive. I've always assumed that events were containers, womewhat like List<> that contained delegates.. is that a wrong assumption?
Mystere Man
@Mystere Man - worse: an unsubscribed event is `null`, so it will fail completely. Your assumption is incorrect.
Marc Gravell
Making assumptions when it comes to locking is a really bad idea. Knowing exactly how the lock works is paramount.
Rusty
@Rusty - indeed; general approach: either keep it **really** simple (`lock` something private and readonly), or get very good at the details of locking.
Marc Gravell
Good point about the null. Yes, that makes more sense. I didn't realize that delegates were immutable, that changes a great deal of how I think about them.
Mystere Man
Regarding your update... Ironically, i got that code from the .net 4 MSDN, guess it hasn't been updated ;) By any chance, has .net 4 made it unnecessary to add the delegate manually when used in an explicit interface?
Mystere Man
You mean, does this work? `event EventHandler IFoo.Bar;` No, it doesn't.
Marc Gravell
+1  A: 

Any private reference type member will do the job. As long as it is private and never gets reassigned. Which knocks a delegate object out of the running, you definitely don't want to see a lock fail to do its job simply because client code that you don't control assigns an event handler. Extremely hard to debug.

Using private members that do another job is not something that scales well. If you find out you need to lock another region of code while refactoring or debugging, you'll need to find another private member. Which is where things can turn sour quickly: you might pick the same private member again. Deadlock knocking on the door.

This doesn't happen if you dedicate a lock object to a specific set of shared variables that need to be protected. Allows you to give it a good name too.

Hans Passant
This is why I suggested using the event, since that was the "object" you were manipulating. However, if I'm understanding Marc's comments correctly, then using an event as a locking object is a very bad thing...
Mystere Man