Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What exactly means locking on an object?

Altough I am using locks in my app, I do not understand what exactly does locking on particular reference type. I thought it just stops the thread until the content of {} is finished. But I have read that locking(this) is bad, if its public - why? The article explained it but I do not understand sice I do not know what happened to the object itself being locked. For example, what if I use lock(this) and from another thread call its method? I thought only the code under the lock is protected, or I will be unable to access the locked object at all? Thanks

like image 300
Thomas Avatar asked Jan 01 '10 09:01

Thomas


2 Answers

Each object on the managed heap can be used as a lock-object, which is a means to synchronize access between threads.

I thought it just stops the thread until the content of {} is finished.

Well, a lock it stops other threads from acquiring the lock until the lock is released, which is most commonly at the end of a lock statement (but it could also be a Monitor.Wait).

The lock(this) usage is dangerous because locking is complex, and understanding exactly which threads are locking which object(s) at which time is very important to avoid deadlocks; however, if you lock(this) you aren't in control of other threads - which could also be (unexpectedly) locking the same object. It is far safer to use a private field for locking.

As an example, if you have (in a synchronized list):

private IList<T> innerList = ...
public int Count { get { lock(this) { return innerList.Count; } } }

Then it isn't hard to imagine another bit of code also having a reference to this synchronized list, and locking on it, for example:

SyncList<T> list = ...
lock(list) { // lock for atomicity
    if(!list.Contains(value)) list.Add(value);
}

Which is a potential deadlock; it would be much better if Count didn't lock(this), but locked a private object, i.e.

private readonly object syncLock = new object();
public int Count { get { lock(syncLock) { return innerList.Count; } } }

Now there is no risk of this issue. Another issue here is that both field-like events and [MethodImpl] cause lock(this). Locking on a Type (for static methods) is equally dangerous for exactly the same reasons.

like image 147
Marc Gravell Avatar answered Oct 13 '22 06:10

Marc Gravell


The object itself isn't locked. Think of each object as having an associated lock (or monitor). When one thread has acquired the lock, no other thread can acquire it without the first thread releasing it, either by calling Monitor.Exit (which is what happens at the end of a lock statement) or by calling Monitor.Wait. A thread calling Monitor.Enter to acquire the lock will block until it can acquire the lock.

The object itself isn't "protected" at all - locks are advisory, basically.

The reason for not locking on "this" is that you don't know what other code has a reference to "this". Getting locking right requires you to know in which situations a thread will or won't own the lock - and you can't know that if code outside your control can take out the lock. The exception to this is if you're exposing a reference for the explicit purpose of sharing a lock (like the SyncRoot property on .NET 1.1 collections).

like image 37
Jon Skeet Avatar answered Oct 13 '22 06:10

Jon Skeet