3

Consider the following code:

static void AddItem()
{
    lock (_list) 
        _list.Add ("Item " + _list.Count); //Lock 1

    string[] items;
    lock (_list) 
        items = _list.ToArray(); //Lock 2
    foreach (string s in items) 
        Console.WriteLine (s);
}

If Thread A gets Lock 2, and Thread B attempts to get Lock 1, will B get the lock or not? Considering both locks use the same locking object.

spurra
  • 1,007
  • 2
  • 13
  • 38

5 Answers5

5

No, thread B will need to wait until thread A releases the lock. That's the point of it being the same lock object, after all - there's one lock. Where the lock is acquired or released is irrelevant: only one thread can "own" the monitor at a time.

I would strongly advise you to use braces for readability, by the way:

lock(_list)
{
    _list.Add(...);
}
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
2

No, since they use the same locking object, they are mutually exclusive.

Often code is used to lock an object (for example a list) to perform an operation on it without interference from other threads. This requires that the item is locked no matter what operation is performed.

To elaborate, say you have a list that is designed to be threadsafe. If you try adding and deleting multiple items simultaneously, you could corrupt the list. By locking the list whenever it needs to be modified, you can help ensure thread safety.

This all depends on the fact that one object will make all locks mutually exclusive.

Kendall Frey
  • 43,130
  • 20
  • 110
  • 148
2

No, B will not. Both are locking on the same object and therefore the two locks are "linked." For this reason, if you need to highly-optimise such code, there are times where you might consider multiple lock objects.

As a side note, you should not be locking on the list itself but on an object created specifically for that purpose.

Dan Puzey
  • 33,626
  • 4
  • 73
  • 96
  • Why shouldn't you lock on the list itself? As long as it's a reference type, you will be fine. – Kendall Frey Aug 14 '12 at 12:53
  • It's not guaranteed to be fine at all; you have no idea how or where `_list` is defined. Bad practises are always best avoided, even when they happen to be safe in a given (restricted) scenario. See [this question](http://stackoverflow.com/questions/11775205/why-is-it-a-bad-practice-to-lock-the-object-we-are-going-to-change/11775353#11775353). – Dan Puzey Aug 14 '12 at 12:56
  • The important thing is not to have separate object, but to have an object you know won't be used for any other locking. (Which the old `SyncRoot property` idea got wrong, in encouraging people to lock on something other code could be locking on). If you can **easily** see this is true of `_list` then the self-documenting nature is a win IMO. – Jon Hanna Aug 14 '12 at 13:07
  • @JonHanna: sometimes you need to make more than one object threadsafe "together" - if your thread-safe operation touches more than one variable, locking on one of those variables isn't "self-documenting." And there are reasons that Microsoft recommend against this practise! You can't easily *guarantee* that nobody will change the use of `_list` later on; a purpose-declared object is much less likely to be corrupted by later edits. – Dan Puzey Aug 14 '12 at 13:12
  • If there isn't a self-documenting choice, then "go for the self-documenting choice" isn't a bad plan, it's just one that won't come up with an answer, and will force you to pick something else. It's also not self-documenting if you can't grok the whole small class in one go. Separate lock objects are generally the way to go. A more crucial exception is if you have between zero and a couple of thousand objects that need the operation on them to be locked. Then I'd definitely lock on the object - and go out of my way to make sure nothing else could. – Jon Hanna Aug 14 '12 at 13:17
1

If Thread A is using the lock, then no other thread can use it (regardless of where the lock is being used). So, thread B will be blocked until that lock is free.

Dave New
  • 38,496
  • 59
  • 215
  • 394
1

Consider that:

lock(obj)
{
 //Do Stuff;
}

Is shorthand for:

Monitor.Enter(obj);
try
{
  //Do Stuff;
}
finally
{
  Monitor.Exit(obj);
}

Now consider that Monitor.Enter() is a method call like any other. It knows nothing about where in the code it was called. The only thing it knows about, is what object was passed to it.

As far as it's concerned, what you call "Lock 1" and "Lock 2" are the same lock.

Jon Hanna
  • 110,372
  • 10
  • 146
  • 251
  • @Falcon that's pretty much what `lock` means. – Jon Hanna May 30 '13 at 10:47
  • I get that. I have seen it on many examples. However, I would like to find a way to "decompile" lock statement into what is underneath. I can do that with il disassembler, but I am not getting same results. I am looking for a tool to do that. – Falcon Jun 04 '13 at 12:14