2010-10-15 5 views
14

Estoy escribiendo una aplicación GUI.¿Cómo cierro correctamente una aplicación C# que ha creado varios subprocesos?

La aplicación está abriendo varios hilos durante su vida útil. Uno de los subprocesos es el manejo de eventos que pueden provenir de otras aplicaciones, por lo que está esperando en un ciclo while (verdadero) para el evento que nunca se termina.

El usuario puede cerrar la aplicación en cualquier minuto. Quiero cerrar todos los hilos que la aplicación principal había abierto.

Estoy usando Process.GetCurrentProcess(). Kill(); para lidiar con este problema en este momento.

¿Es esta una buena solución? Si no es así, ¿por qué y cuál es la forma correcta de tratar este problema, cómo cerrar todos los hilos abiertos por la aplicación principal?

+0

Debe evitar el trabajo inactivo de 'while (true)'. Al menos agregue un 'Sleep (100)' para darle un poco de espacio a su CPU. Mire algunos libros gratuitos como http://www.albahari.com/threading/ o http://www.csharpcourse.com/ para obtener detalles sobre cómo mantener adecuadamente un hilo vivo sin hacer un trabajo inactivo. – ja72

+1

El ciclo while (verdadero) está esperando los eventos. Como tiene un waitOne de bloqueo(), no está usando brutalmente mi CPU. – Alex

Respuesta

23

Si crea los nuevos hilos como hilos de fondo (estableciendo IsBackground antes de iniciarlos), se detendrán automáticamente cuando termine el hilo principal (el hilo de la aplicación).

(De MSDN):

Un hilo es o bien un hilo de fondo o un hilo plano. Los subprocesos de fondo son idénticos a los subprocesos de primer plano, excepto que los subprocesos de fondo no impiden que un proceso finalice. Una vez que todos los hilos de primer plano que pertenecen a un proceso han terminado, el tiempo de ejecución de lenguaje común finaliza el proceso. Los hilos de fondo restantes se detienen y no se completan.

+1

El ajuste 'IsBackground = true' suena como si fuera apropiado para su caso. Tuve que usar esa propiedad cuando tenía un hilo que bloqueaba continuamente en 'Console.ReadLine' y esperaba la entrada. * Sin embargo, * en la mayoría de los casos, debe intentar diseñar su programa para que no permita el cierre hasta que se completen los procesos de cálculo o procesamiento, o puede crear un mecanismo de cancelación en el código de cálculo o procesamiento. – jnylen

+0

Estoy de acuerdo con @jnylen, quiere tener algún tipo de mecanismo para permitir que sus subprocesos salgan con gracia, no solo para apagarlo mientras posiblemente en el medio de alguna operación. – CodingGorilla

+0

@jnylen: Sí, si los hilos están haciendo algo que ** requiere ** una terminación limpia (por ejemplo, escribir en un archivo), necesita una terminación limpia indicándoles que se detengan y luego esperen. Si los hilos se pueden interrumpir, bastará con establecerlos en un hilo de fondo be. – adrianbanks

2

Una vez que ya tenga subprocesos esperando por algunos eventos, simplemente agregue un evento más que cuando se desencadene instruirá al hilo para que termine.

En caso de que no es necesario disponer de algún medio de apagado ordenado de otros hilos, puede cambiarlos en el modo de “hilo de fondo” para garantizar la terminación automática - see MSDN para una discusión a fondo de este tema.

1

Hay muchas maneras de lidiar con esto, pero lo ideal es que quiera que sus hilos salgan normalmente por su cuenta en lugar de simplemente matar el proceso.

Se podría hacer algo muy simple como esto:

public class ThreadSignal 
{ 
    public bool Stop { get; set; } 
} 

Luego, en su bucle de hilo, hacer:

public void DoWork(object state) 
{ 
    ThreadSignal signal = (ThreadSignal)state; 
    while(!signal.Stop) 
    { 
     // Do work here  
    } 
} 

Luego, cuando esté listo para parar, establecer su ThreadSignal.Stop a true. Este es un ejemplo muy simple, pero te da un punto de partida.

0

Como mencionaste es una aplicación de GUI, por lo que el hilo principal que es responsable del bucle de mensajes es responsable de alertar al bucle infinito (while(true)) que el usuario quiere salir del programa. Recomiendo reemplazar true con otro boolean para señalar que el usuario ha cerrado la ventana de esta manera: while(windowIsOpen) y establecerlo en falso en la descarga de su formulario.

1

Debe esperar en el bucle con un evento manualResetEvent (o AutoResetEvent).A continuación, sólo establecer una variable miembro para cierto cuando va a apagar:

public class MyForm : Form 
{ 
    private AutoResetEvent _workTrigger = new AutoResetEvent(); 
    private bool _shuttingDown = false; 
    private Thread _thread; 

    public void Form_Initialize() 
    { 
     _thread = new Thread(MyThreadMethod); 
     _thread.Start(); 
    } 

    public static void MyThreadMethod(object State) 
    { 
     while (!_shuttingDown) 
     { 
      //wait for jobs. 
      _workTrigger.WaitOne(); //can add a timeout as parameter. 

      //do some work here 

     } 

    } 


    public void Form_Closing(object source, EventArgs e) 
    { 
     _shuttingDown = true; 
     _workTrigger.Set(); 

     //wait for it to exit. You could use the timeout 
     //parameter and a loop to not block the UI 
     _thread.Join(); 
    } 
} 
0

no pierda sus discusiones en torno a la aplicación - keep'em en alguna parte (List<Thread> podría servir). Luego, cuando sea el momento adecuado (hora de cierre) notifique a cada uno que debe terminar lo que está haciendo y salir.

Entonces, .Join() todos ellos, a continuación, permitir la aplicación para salir.

No vuelvas a ir al reino 'ThreadAbort', que es el lado oscuro de la fuerza que se esconde allí.

0

general cómo hacer esto es:.

  • crear una clase que encapsula este comportamiento (por ejemplo, el manejo de los mensajes entrantes en el fondo
  • Tener la clase herede de IDisposable Cuando se llama a Dispose() establece una variable privada denominada _disposed
  • crear mi hilo dedicado en mi constructor de la clase.
  • Tener un AutoResetEvent privado llamado _workToDo. Su hilo de fondo esperará en este caso y sólo hacer un bucle trabajo cuando se señaliza este evento.
  • tiene un método público para enviar el mensaje a su trabajador de fondo que pone en cola el trabajo y entonces fija _workToDo de decirle a su subproceso de fondo para hacer el trabajo.

Poniendo todo esto junto, se obtiene:

public class BackgroundProcessor : IDisposed 
{ 
    private Thread _backgroundThread; 
    private bool _disposed; 
    private AutoResetEvent _workToDo = new AutoResetEvent(false); 
    // where T is a class with the set of parameters for your background work 
    private Queue<T> _workQueue = Queue.Synchronized(new Queue<T>); 

    public BackgroundProcessor() 
    { 
    _backgroundThread = new Thread(DoBackgroundWork); 
    _backgroundThread.Start(); 
    } 

    public void Dispose() 
    { 
    _disposed = true; 

    // Wait 5 seconds for the processing of any previously submitted work to finish. 
    // This gives you a clean exit. May want to check return value for timeout and log 
    // a warning if pending background work was not completed in time. 
    // If you're not sure what you want to do yet, a Debug.Assert is a great place to 
    // start because it will let you know if you do or don't go over time in general 
    // in your debug builds. 
    // Do *not* Join() and wait infinitely. This is a great way to introduce shutdown 
    // hangs into your app where your UI disappears but your process hangs around 
    // invisibly forever. Nasty problem to debug later... 
    Debug.Assert(_backgroundThread.Join(5000)); 
    } 

    // Called by your 'other application' 
    public void GiveMeWorkToDo(T workParameters) 
    { 
    _workQueue.Enqueue(workParameters); 
    _workToDo.Set(); 
    } 

    private void DoBackgroundWork() 
    { 
    while (!_disposed) 
    { 
     // 500 ms timeout to WaitOne allows your Dispose event to be detected if there is 
     // No work being submitted. This is a fancier version of a Thread.Sleep(500) 
     // loop. This is better because you will immediately start work when a new 
     // message is posted instead of waiting for the current Sleep statement to time 
     // out first. 
     _workToDo.WaitOne(500); 

     // It's possible multiple sets of work accumulated or that the previous loop picked up the work and there's none left. This is a thread safe way of handling this. 
     T workParamters = _workQueue.Count > 0 ? workParameters = _workQueue.Dequeue() : null; 
     do 
     { 
     DoSomething(workParameters); 

     workParameters = _workQueue.Count > 0 ? workParameters = _workQueue.Dequeue() : null; 
     } while (workParameters != null) 
    } 
    } 
} 
0

considerar el uso de la clase BackGroundWorker. Como usa el grupo de subprocesos (a través de BeginInvoke()), obtendría subprocesos de fondo. Como bonificación, obtienes notificaciones de progreso, cancelación y finalización de avance convenientes (ya incluidas en el hilo de la interfaz de usuario).

Cuestiones relacionadas