Posted By: Dr Herbie | Aug 7th @ 12:24 AM
page 1 of 1
Comments: 13 | Views: 564
Dr Herbie
Dr Herbie
Horses for courses

Ok, I'd like people's opinions on the use of the lock() keywork in C#. I've never quite got the subtleties of threading (no CS degree!).

 

The scenario is that I have a class that contains a Dictionary<string, object> that is referenced by several other classes.  I'd like to make assess to that dictionary thread safe.

 

The 'standard' way to do this is to use a separate lock object

 


class MyClass 

{

    Dictionary<string, object> _store = new Dictionary<string, object>();

    object _lock = new object();

 

    public void Add(string key, object value)

    {

        lock(_lock) 

        {

            _store.Add(key, value); 

        }

    }

}

 

 

It has occurred to me, that perhaps I should lock the target object itself, thus doing away with the added lock object:

 


class MyClass 

{

    Dictionary<string, object> _store = new Dictionary<string, object>(); 

 

    public void Add(string key, object value) 

    {

        lock(_store) 

        {

            _store.Add(key, value); 

        }

    }

}

 

Is this safe?

 

 

Herbie 

 

 

EDIT:  Why does the code formatting suck?  Where do these double-spaced lines come from?

Simo
Simo
With me it's a full-time job.

I guess locking the dictionary object vs some arbitrary object is going to depend on whether you only care about managing the access to the dictionary or all of your MyClass state.

 

If it's just the dictionary then I think locking the SyncRoot property is probably considered good form. http://msdn.microsoft.com/en-us/library/bb348148.aspx

That seems fine except if you go back and later change your code to set _store to null or a different object.  Declare it readonly to prevent and discourage this.

TommyCarlier
TommyCarlier
I want my scalps!

I always use lock on an object, specifically created for that purpose (like your first example). I'm still wondering why they made every object lockable, instead of adding a specific Lock-type.

Sven Groot
Sven Groot
My name has 9 letters. Coincidence? I think not...

Because with a lock you're saying, "I want to protect multi-threaded access to this object", so you have to say which object you're protecting. By using a separate lock object, you're using one object to protect access to another, which to me breaks the semantics of the lock keyword (Monitor class). You're also creating an extra object with no purpose other than to take up memory.

 

The only reason I'd ever use a separate lock object is if I need to have a lock around the creation of an object.

 

So in Herbie's example, I'd just lock the dictionary, or its syncroot. And I'd mark the _store field readonly so you're sure you can't accidentally overwrite it.

stevo_
stevo_
Human after all

Isn't the point so you can give context to the lock, as in you could have two locks to use one object in different contexts.

Ewww, that sounds horrible. And ever so likely to start causing unintentional race conditions.

TommyCarlier
TommyCarlier
I want my scalps!

But lock is not meant to protect multi-threaded access to an object, it's meant to protect blocks of code from not executing at the same time. The lock on an object does nothing to the object itself. Lock is, in my opinion, a badly chosen term.

stevo_
stevo_
Human after all

Uh no.. because you may well be doing two operations that are completely split.. just as if this were two completely split locks anywhere.. forcing to lock on the actual object would make it impossible to contextually lock.

TommyCarlier said:
But lock is not meant to protect multi-threaded access to an object, it's meant to protect blocks of code from not executing at the same time.

But the reason for not wanting two blocks of code to execute at the same time is to avoid multiple threads from updating shared state, so Sven's point stands.

 

stevo_ said:
Uh no.. because you may well be doing two operations that are completely split..

You have to be very, very careful trying to do that, it all to easily leads to situations where you think some shared state is being locked but in actual fact is being altered in some subtle way you hadn't considered. There are usually better synchronization primitives than lock when in those sorts of scenario. Arguably, with the possible exception of locking part of a collection (which is a whole different kettle of fish anyway), even being in that situation is a sign your object should be broken up.

stevo_
stevo_
Human after all

Note that I'm not advocating it, purely stating the design allows this to be done.. which makes it argably more powerful for a gp language.

I normally just lock the object I'm trying to protect. I don't see why the result would be any different between the two options, except that you're using more memory with the first.

Fair enough. It also allows you to create a very course grained locking scenario, where you just have one lock that needs to be held before modifying any one of a number of bits of shared state. Whilst that may not lead to the most performant solution, if the majority of code doesn't rely on shared state it can be considerably easier than trying to get a fine-grained locking scenario working without accidentally creating deadlocks.

page 1 of 1
Comments: 13 | Views: 564
Microsoft Communities