user1312703
user1312703

Reputation:

Can I dispose locked object?

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

Answers (5)

Jeroen van Langen
Jeroen van Langen

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

Brian Gideon
Brian Gideon

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

Ben
Ben

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

Alex Shtoff
Alex Shtoff

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

Douglas
Douglas

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

Related Questions