GC.KeepAlive versus using

2020-02-28 03:07发布

问题:

In his article about preventing multiple instances of an application, Michael Covington presents this code:

static void Main()                  // args are OK here, of course
{
    bool ok;
    m = new System.Threading.Mutex(true, "YourNameHere", out ok);

    if (! ok)
    {
        MessageBox.Show("Another instance is already running.");
        return;
    }

    Application.Run(new Form1());   // or whatever was there

    GC.KeepAlive(m);                // important!
}

He explains that the GC.KeepAlive(m) is required to prevent the garbage collector from collecting the mutex early, since there are no additional references to it.

My question: will wrapping the mutex in a using do the same thing? That is, will the following also prevent the GC from pulling the rug out from under me?

static void Main()                  // args are OK here, of course
{
    bool ok;
    using (var m = new System.Threading.Mutex(true, "YourNameHere", out ok))
    {
        if (! ok)
        {
            MessageBox.Show("Another instance is already running.");
            return;
        }

        Application.Run(new Form1());   // or whatever was there
    }
}

My gut reaction is that the using will work, since using is (supposed to be) equivalent to:

Mutex m = new System.Threading.Mutex(true, "YourNameHere", out ok);
try
{
    // do stuff here
}
finally
{
    m.Close();
}

And I would think that the m.Close() there would be enough to signal to the JIT compiler that there's another reference, thus preventing premature garbage collection.

回答1:

Wrapping the mutex in a using statement will indeed prevent it from being garbage collected, but will also dispose of it (it calls Dispose, not Close) at the end (whereas GC.KeepAlive won't, obviously).

If the end of the method is genuinely going to be the end of the process, I don't believe it's likely to make any practical difference which you use - I prefer the using statement on the general principle of disposing of anything which implements IDisposable.

If the mutex hasn't been disposed when the process exits, I suspect its finalizer will take care of it - so long as other finalizers don't hog the finalization thread beyond its timeout.

If the finalizer doesn't take care of it, I don't know whether Windows itself will notice that the process can't possibly own the mutex any more, given that it (the process) doesn't exist any more. I suspect it will, but you'd have to check detailed Win32 documentation to know for sure.



回答2:

Using using seems to be a better fit in this case than using GC.KeepAlive. You not only want to keep the Mutex alive as long as the application is running, you also want it to go away as soon as you exit the main loop.

If you just leave the Mutex hanging without disposing it, there may be some time before it's finalized, depending on how much cleanup work there is to do when the application is shutting down.



回答3:

I think the reason KeepAlive is used with the named Mutex is to make sure it IS NOT garbage collected early. The C# using / Dispose pattern is not for protected against that. When an object reference is no longer used within a scope the runtime may collect it before the end of the scope. It's an optimiztion.