views:

170

answers:

3

Is there a common way to "share" a lock between different objects operating on same set of data?

I am aware that having a public object for locking is usually not recommended.

For example, a Queue could be implemented as thread safe, but some other class might need a specific lock in order to lock several Queue operations. What happens if I have a third class, which also needs to make several locked operations on this same Queue instance?

For example: (let's say that L<T> is a thread-safe list, just to save some typing)

class Worker
{
    private readonly L<Something> _list;
    public Worker(L<Something> list) { _list = list; }

    private readonly object _lock = new object();
    public void Replace(Something old, Something new)
    {
       lock (_lock) 
       {
          if (_list.Contains(old))
          {
              _list.Remove(old);
              _list.Add(new);
          }
       }
    }
}

If some other class, on a different thread, removes the old element just after the if condition, list will no longer contain the element, as _lock is a private object.

Should I lock on the actual list instance?

+7  A: 

Don't expose this list as a property, only the methods to interact with it. Then you can handle all of the locking within the one class and not deal with a public lock object.

ck
-1 for incorrect capitalisation at the start of the second sentence.
Robert Grant
I see your point, but in that case, all methods which interact with that data must belong to the same class. You cannot have a different class which does a sequence of operations, and is also thread safe.
Groo
@Robert - you know you can edit the post yourself, right?
Groo
@Groo re:Robert Grant - Its OK, he's a local fool.
ck
Also I believe you have to have 2000 rep for that.
Robert Grant
+6  A: 

A common way is to expose a property such as ICollection.SyncRoot. Of course, everyone has to obey the lock in order for this to work.

If you can possibly avoid this, and encapsulate the operations as ck suggests, that will be a lot more robust and easy to understand.

Jon Skeet
Thanks, that is the right answer to my question: a common way to share a lock. I am aware of the implications but just wanted to see if there is a third possibility which I am not aware of.
Groo
+3  A: 

Technically you can easily expose the lock object. The problem with this is that the real reason for the recommendation of not making them public is the deadlocks.

If the lock object is exposed it introduces the risk that some other code will lock on this object with no regard to the locking ordering. This in turn can lead to deadlocks.

The same danger exists even if the locks are not exposed explicitly.

The best approach is not to expose the locks at all - neither implicitly nor explicitly. In other words bury them completely inside the class providing services. Unfortunately sometimes this is not an option.

mfeingold