c++ - create thread
¿Cómo garantizo el cierre rápido de mi aplicación win32? (11)
Si necesita cerrar repentinamente: Simplemente llame a ExitProcess, que es lo que se llamará tan pronto como regrese de WinMain de todos modos. Windows mismo crea muchos subprocesos de trabajo que no tienen forma de limpiarse; terminan por el cierre del proceso.
Si tiene hilos que están realizando escrituras de algún tipo, obviamente, esos necesitan una oportunidad de cerrar sus recursos. Pero cualquier otra cosa: ignora las advertencias del corrector de límites y simplemente quita la alfombra de debajo de sus pies.
Tengo una aplicación C ++ Win32 que tiene un número de subprocesos que pueden estar ocupados haciendo IO (llamadas HTTP, etc.) cuando el usuario quiere cerrar la aplicación. Actualmente, juego bien y espero a que todos los hilos terminen antes de volver del main
. A veces, esto lleva más tiempo del que me gustaría y, de hecho, parece inútil hacer que el usuario espere cuando pueda simplemente salir. Sin embargo, si continúo y vuelvo desde main
, es probable que sufra bloqueos a medida que los destructores comienzan a recibir llamadas mientras todavía hay hilos usando los objetos.
Entonces, reconociendo que en un mundo de virtudes ideal y platónico, lo mejor sería esperar a que todos los hilos salgan y luego se apaguen limpiamente, ¿cuál es la próxima mejor solución del mundo real? Simplemente hacer que los hilos salgan más rápido puede no ser una opción. El objetivo es lograr que el proceso muera lo más rápido posible para que, por ejemplo, se pueda instalar una nueva versión. El único disco IO que estoy haciendo es en un DB transaccional, así que no estoy muy preocupado por desconectarlo.
Recomiendo tener su GUI y trabajar en diferentes hilos. Cuando un usuario solicita un cierre, descarte la GUI de forma inmediata dando la apariencia de que la aplicación se ha cerrado. Permita que los hilos del trabajador se cierren con gracia en el fondo.
Puede llamar a TerminateProcess: esto detendrá el proceso de inmediato, sin notificar a nadie y sin esperar nada.
Hagas lo que hagas, NO uses TerminateThread, especialmente en cualquier cosa que pueda estar en las llamadas HTTP del sistema operativo. Podrías potencialmente romper IE hasta que reinicies.
Cambie todos sus IO a un modelo asíncrono o sin bloqueo para que puedan ver los eventos de finalización.
Indique al usuario que desconecte la computadora. A falta de eso, debes abandonar tus actividades asincrónicas al viento. ¿O es eso HWIND? Nunca puedo recordar en C ++. Por supuesto, podría tomar el camino intermedio y anotar rápidamente en un archivo de texto o clave de registro qué acción se abandonó para que la próxima vez que se ejecute el programa pueda reanudar esa acción automáticamente o preguntar al usuario si así lo desea. Dependiendo de qué datos pierda cuando abandone la acción de sincronización, es posible que no pueda hacer eso. Si está interactuando con el usuario, puede considerar un diálogo o alguna interacción de UI que explique por qué tarda tanto.
Personalmente, prefiero las instrucciones al usuario para que simplemente desconecte la computadora. :)
La mejor manera: haga su trabajo mientras la aplicación se está ejecutando, y no haga nada (o tan cerca de) al apagar (también funciona para el inicio). Si se apega a ese patrón, puede eliminar los hilos inmediatamente (en lugar de "ser amable" al respecto) cuando la solicitud de cierre llega sin preocuparse por el trabajo que aún debe realizarse.
En su situación específica, es probable que deba esperar a que IO termine (escribe, al menos) si está realizando un trabajo local allí. Las solicitudes HTTP y las que probablemente solo puedas abandonar / cerrar directamente (de nuevo, a menos que estés escribiendo algo). Pero si es el caso de que esté escribiendo durante este cierre y esperando eso, entonces es posible que desee notificar al usuario de eso, en lugar de dejar que su proceso se vea colgado mientras termina.
Si quiere desconectar el enchufe, la salida (0) hará el truco.
Una vez tuve un problema similar, aunque en Visual Basic 6: los hilos de una aplicación se conectaban a diferentes servidores, descargaban algunos datos, realizaban algunas operaciones en bucle sobre esos datos y almacenaban en un servidor centralizado el resultado.
Entonces, el nuevo requisito era que los hilos deberían poder detenerse desde la forma principal. Logré esto de una manera fácil aunque sucia, haciendo que los hilos se detengan después de N bucles (equivalentes a aproximadamente medio segundo) para tratar de abrir un mutex con un nombre específico. Tras el éxito, inmediatamente detuvieron lo que estaban haciendo y se dieron por vencidos, de lo contrario continuaron.
Este mutex fue creado solo por la forma principal, una vez que se creó, todos los hilos se cerrarían pronto. La desventaja era que el usuario necesitaba especificar manualmente que quería ejecutar los subprocesos otra vez; otro botón para "Permitir que los subprocesos se ejecuten" logró esto liberando el mutex: D
Se garantiza que este truco funciona para las operaciones mutex son atómicas. El problema es que nunca está seguro de que un hilo realmente se haya cerrado: una falla en la lógica de manejo del caso "openMutex tuvo éxito" podría significar que nunca termina. Tampoco sabe cuándo / si todos los hilos se han cerrado (suponiendo que su código es correcto, esto tomaría más o menos el mismo tiempo que les lleva a los bucles detenerse y "escuchar").
Con el modelo "multi-threading" de VB, es algo difícil enviar y recibir información de los hilos de la aplicación principal, es mucho más fácil "disparar y olvidar" o enviarlo solo desde la aplicación principal al hilo. Por lo tanto, la necesidad de este tipo de cortes largos. Al usar C ++, puedes utilizar tu modelo de subprocesos múltiples, por lo que es posible que estas restricciones no se apliquen a ti.
Use IO superpuesto para que siempre tenga el control de los hilos que están tratando con su E / S y siempre puede detenerlos en cualquier punto; o los tiene esperando en un IOCP y pueden publicar un código de cierre de nivel de aplicación, O puede esperar el evento en su estructura OVERLAPPED Y esperar su evento ''all threads please shutdown now'' también.
En resumen, evite bloquear llamadas que no puede cancelar.
Si no puede y está atrapado en una llamada de socket de bloqueo haciendo IO, entonces siempre puede cerrar el socket del hilo que ha decidido que es hora de cerrar y tener el hilo que está haciendo IO siempre verifica el ''cierre ahora ''evento antes de volver a intentar ...
Utilizo una técnica basada en excepciones que me ha funcionado bastante bien en varias aplicaciones Win32.
Para finalizar un hilo, uso QueueUserAPC()
para poner una llamada en cola a una función que arroja una excepción. Sin embargo, la excepción que se lanza no se deriva del tipo "Excepción", por lo que solo será captada por el procedimiento de reinicio de mi hilo.
Las ventajas de esto son las siguientes:
- No se necesita ningún código especial en el hilo para que sea ''inaccesible'': tan pronto como ingrese en un estado de espera alertable, ejecutará la función APC.
- Todos los destructores se invocan cuando la excepción se ejecuta en la pila, por lo que su hilo sale limpiamente.
Las cosas que debes vigilar:
- Cualquier cosa que
catch (...)
se comerá su excepción. ¡El código de usuario siempre debe usarcatch(const Exception &e)
o similar! - Asegúrese de que su E / S y retrasos se realicen de una manera "alertable". Por ejemplo, esto significa llamar a
sleepex(N, true)
lugar de asleep(N)
. - Los subprocesos vinculados a CPU necesitan llamar
sleepex(0,true)
asleepex(0,true)
para verificar la terminación.
También puede "proteger" áreas de su código para evitar la finalización de tareas durante las secciones críticas.
*NULL = 0
es la manera más rápida. si no desea bloquearse, llame a exit()
o su equivalente win32.