Cómo salir completamente de la DLL multiproceso en la plataforma Windows
Recientemente estoy desarrollando complementos para reproductores. Basado en los marcos directshow, vlc y mplayer, creé un complemento. Los otros tres complementos usan la biblioteca DLL multimedia (Mylib.dll), que se usa a través de bibliotecas cargadas dinámicamente. La DLL es más complicada y utiliza subprocesos internamente. Además, los complementos para directshow y vlc también son DLL; mplayer no admite complementos dinámicos, pero tiene compilación de código fuente incorporada.
Cuando no se gestiona la salida del proceso, los tres reproductores (prueba del reproductor basado en directshow GraphEidt y Windows Media Player) no pueden salir normalmente.
Al observar la pila de llamadas de subprocesos, cuando finalmente se descargó Mylib.dll, un destructor de objetos global del dll esperó un objeto de evento y no regresó. Este evento debería establecerse en una señal antes que otra. El hilo sale, pero todo el proceso, excepto el hilo principal, ha finalizado, lo que muestra que el hilo se vio obligado a finalizar. La pila de llamadas del hilo principal muestra que ha ingresado a _ExitProcess. Debería ser que el hilo principal haya eliminado todos los hilos secundarios antes de esto.
Ninguno de los tres frameworks de reproductor ha encontrado un mecanismo de notificación de salida, por lo que la única forma que se nos ocurre es usar atexit y llamar a una interfaz Mylib.dll Stop (Mylib_Stop) al final del programa. Sin efecto, todavía no puedo salir normalmente.
Pero a diferencia de la situación anterior, mplayer todavía se bloquea en el destructor de objetos global de Mylib.dll; Directshow y vlc se bloquean directamente en la función Mylib_Stop, y la pila de llamadas muestra que está esperando que se inicie otro hilo. salir. Pero este hilo está atascado en _ExitThread.
Para resolver el problema del punto muerto en la destrucción global de objetos, el hilo debe salir normalmente en lugar de terminarse a la fuerza, por lo que Mylib.dll debe descargarse antes de _ExitProcess. Agregamos esta tarea al proceso atexit porque se llamará a la función de atexit después de salir de la función principal. Después de la modificación, mplayer puede salir normalmente.
Para directshow y vlc, Mylib.dll se inicia en el complemento DLL. Descubrimos que la función registrada por atexit se llama al descargar el DLL del complemento y hay una función DllMain en la pila de llamadas. Teniendo en cuenta algunos detalles de la ejecución de la función DllMain, creo que la situación debería ser así: cuando el sistema llame a DllMain, habrá un bloqueo global, el hilo principal ha ingresado a DllMain, se han agregado todos los bloqueos y también se llamará a DllMain. cuando sale otro hilo, este bloqueo también es necesario, lo que hace que el hilo principal espere a que salga otro hilo, y este hilo espera el bloqueo ocupado por el hilo principal, formando una situación de punto muerto.
Además, la función registrada por atexit en la DLL no se llama cuando el proceso sale como esperábamos, y también se llama cuando se descarga la DLL correspondiente. MSDN no explica esto. Sin embargo, al rastrear atexit, descubrimos que efectivamente se procesó de acuerdo con DLL o EXE, y también descubrimos que la función _ _imp___onexit se usó en EXE.
Intenta reemplazar atexit con _ _imp___onexit. No hay problema al compilar, pero se bloqueará cuando se ejecute, porque el código de registro para salir de la función de ejecución está en realidad en la DLL del complemento, y esta DLL se descargó antes de que finalice el proceso y la página de códigos no es válida.
Al final, solo puedo modificar Mylib.dll para liberar el punto muerto de espera de eventos en la destrucción de variables globales. Puede acomodar tres jugadores sin usar atexit, el costo es que Mylib.dll no termina. normalmente y los hilos internos están forzados. Si se termina, puede haber algún trabajo posterior que no se pueda completar.
El resumen es el siguiente: 1. Si una DLL con subprocesos internos quiere salir normalmente, necesita exportar una interfaz de función de salida para que la persona que llama la llame en el momento adecuado.
2. Cabe señalar que es posible que el hilo haya sido finalizado por la fuerza antes de que se destruyan las variables globales en la DLL. Si depende de subprocesos para completar algún trabajo en el destructor, debe considerar esta posibilidad, pero no hay problema en esperar directamente el identificador del subproceso.
3. Las funciones registradas con atexit en el código DLL no se pueden llamar cuando el proceso sale. Además, sin importar el código DLL o EXE, las funciones de otros módulos no se pueden registrar con atexit.