Reputation:
What happens if I call Dispose()
on locked object?
lock (obj)
{
obj.Dispose();
}
And what happens if I skip Monitor.Exit()
call in this case:
Monitor.Enter(obj);
obj.Dispose();
Upvotes: 7
Views: 4997
Reputation: 22083
You can always lock on a existing object, The Dispose
method won't delete your object, so the object's reference still exists. The Dispose()
method isn't affecting the instance of the object.
Upvotes: 2
Reputation: 48959
What happens if I call Dispose() on locked object?
First, the object itself is not locked (protected) to begin with. The reference used in the lock
keyword is used to mark or tag a section of the code that should not run simultaneously with any other (or same) section of code that used the same object reference. It does not actually affect the object itself. This is a pretty common misconception about how locks work in .NET.
In this context calling Dispose
is no different than calling any other method. There is nothing special that happens. It just means that two different threads cannot be executing Dispose
simultaneously. What you are doing is not only acceptable, but is actually recommend if the class is not thread-safe.
And what happens if I skip Monitor.Exit() call in this case:
You should always release locks. Keeping in mind that locks do not act upon the object reference itself it should be easier to understand that you would be leaving a lock in an acquired state. It does not matter that the object is disposed. Remember, Monitor
(or lock
) does not lock or protect the object itself. It only marks or tags a section of code. If a thread attempts to acquire a lock with the same object again then that thread will be forced to wait indefinitely possibly resulting in a deadlock.
A more interesting question is whether this could lead to a memory leak. Does Monitor.Enter
root the object? The answer is no. This can be demonstrated with the following example.
public class Program
{
public static void Main(string[] args)
{
var foo = new Foo();
Monitor.Enter(foo);
foo = null;
GC.Collect();
GC.WaitForPendingFinalizers();
Console.ReadLine();
}
}
internal class Foo
{
~Foo()
{
Console.WriteLine("~Foo");
}
}
If you compile and run this you will observe that "~Foo" is printed out. This suggests that Monitor.Enter
does not hold a reference internally. So while it is not advisable to skip the Monitor.Exit
call you can have some comfort in knowing that it is not going to cause a memory leak.1
1Actually, this may not be entirely true. While it is fairly easy to demonstrate that there is no managed memory leak things might be different in the unmanaged realm. Short of looking at the SSCLI code we really do not know what Monitor.Enter
is doing internally. Maybe it is allocating one additional array slot (or whatever) in the unmanaged heap or stack. I would like to think Microsoft considered this obscure scenario, but who knows. The point is that Monitor.Enter
and Monitor.Exit
calls should always be paired so that you do not have to worry about it.
Upvotes: 5
Reputation: 35663
Implementations of Dispose are not generally thread-safe.
This is because you should not dispose of an object while there are still references which may be used. This implies you should not dispose of an object which any other thread holds a reference to.
IDisposable
/Dispose
is not a method of managing object lifetime, either in general or across threads - it is a paradigm for releasing the object's resources once its lifetime is over. (The using
statement is an idiom/paradigm for use where the object's proper lifetime is the scope of the statement.)
So if you are calling dispose while another thread has the object locked, something has already gone very wrong.
Upvotes: 6
Reputation: 2640
For your first question - what happens is that only one threat at any given moment may call Dispose(). For your second question - if multiple threads run this code, the 1st one of them will invoke the Dispose method. The rest will block forever.
There is nothing special in the dispose method. It is just like any other method, except that it participates in some syntactic sugar (using statements, for example).
Upvotes: 1
Reputation: 54907
The Dispose
paradigm is distinct from the lock
one. You can safely dispose an object on which you currently hold a mutual-exclusion lock; however, you should still release the lock afterwards. If you fail to do this, other threads that call Monitor.Enter
on the (now disposed) object would block indefinitely.
Upvotes: 4