2009-03-11 12 views
13

En su article about preventing multiple instances de una aplicación, Michael Covington presenta este código:GC.KeepAlive frente a la utilización

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! 
} 

Explica que la GC.KeepAlive (m) es necesario para evitar que el recolector de basura se acumule el mutex temprana , ya que no hay referencias adicionales al mismo.

Mi pregunta: ¿envolver el mutex en un uso hará lo mismo? Es decir, ¿lo siguiente también evitará que el GC tire de la alfombra debajo de mí?

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 
    } 
} 

Mi reacción visceral es que el uso va a funcionar, ya que el uso está (se supone que) equivalente a:

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

Y yo creo que la m.Close() no sería suficiente para indicarle al compilador JIT que hay otra referencia, evitando así la recolección prematura de basura.

+0

No soy experto en CLR (por lo que no estoy agregando ninguna respuesta) pero me parece que lo que está haciendo debería funcionar. Yo digo, pruébalo y mira. –

+4

Cuando hablamos del GC, "probarlo y verlo" resulta ser una mala idea. He tenido casos en los que las pruebas dicen que debería funcionar, pero fallaron en la producción. Mucho tiene que ver con la diferencia en la forma en que el compilador JIT funciona en modo Release vs. Debug. –

Respuesta

17

Envolver la exclusión mutua en un comunicado using hecho va a evitar que se recoge la basura, sino que también disponer de él (que llama Dispose, no Close) al final (mientras que GC.KeepAlive no será, obviamente).

Si el final del método realmente va a ser el final del proceso, no creo que pueda hacer ninguna diferencia práctica que use - Prefiero la declaración using sobre el principio general de deshacerse de cualquier cosa que implementa IDisposable.

Si no se eliminó el mutex cuando finaliza el proceso, sospecho que su finalizador se encargará de él, siempre que otros finalizadores no acaparen el hilo de finalización más allá de su tiempo de espera.

Si el finalizador no se ocupa de él, no sé si Windows notará que el proceso ya no puede ser el propietario del mutex, ya que (el proceso) no existe. Más. Sospecho que sí, pero tendrías que verificar la documentación detallada de Win32 para estar seguro.

+1

Tiene razón, llama a Dispose en vez de Close, aunque en este caso resulta ser lo mismo (Close calls Dispose). La documentación de SDK dice que el mutex se lanzará al salir del programa. Al igual que usted, prefiero usar por la misma razón. –

+0

Acabo de ver esta pregunta, ya que la llamada a Application.Run (nueva Form1()) no volverá hasta que el proceso se cierre, por lo que la llamada GC.KeepAlive (m) en realidad no es importante, ¿estoy en lo cierto? – Benny

+4

@Benny: Sí, lo es, porque sin esa llamada, el objeto puede ser basura recolectada. El hecho de que exista la llamada a 'GC.KeepAlive' significa que el GC se ve obligado a considerar' m' como una referencia de "raíz". –

5

El uso de using parece ser un mejor ajuste en este caso que el uso de GC.KeepAlive. No solo quiere mantener el Mutex activo mientras se ejecuta la aplicación, también quiere que desaparezca tan pronto como salga del ciclo principal.

Si solo deja colgado el Mutex sin desecharlo, puede pasar un tiempo antes de que se finalice, dependiendo de la cantidad de trabajo de limpieza que haya que hacer cuando se cierre la aplicación.

-2

Creo que la razón por la cual KeepAlive se usa con el nombre Mutex es para asegurarse de que NO se recolecte basura antes. El patrón C# using/Dispose no está protegido contra eso. Cuando una referencia de objeto ya no se utiliza dentro de un alcance, el tiempo de ejecución puede recopilarla antes del final del alcance. Es una optimización.

+1

<< El patrón de C# using/Dispose no está protegido contra eso >> ¿Puede proporcionar pruebas en apoyo de ese reclamo? ¿O un caso de prueba o documentación? gracias! – EricLaw

Cuestiones relacionadas