usar tutorial threads thread libreria ejemplos ejemplo c++ multithreading c++11 exit stdthread

tutorial - threads c++>



¿Qué sucede con un hilo separado cuando sale main()? (5)

Separar hilos

De acuerdo con std::thread::detach :

Separa el hilo de ejecución del objeto de hilo, permitiendo que la ejecución continúe de forma independiente. Cualquier recurso asignado se liberará una vez que el hilo se cierre.

De pthread_detach :

La función pthread_detach () indicará a la implementación que el almacenamiento para el hilo se puede reclamar cuando termina el hilo. Si thread no ha terminado, pthread_detach () no causará que termine. El efecto de múltiples llamadas pthread_detach () en la misma cadena de destino no está especificado.

Separar hilos es principalmente para guardar recursos, en caso de que la aplicación no necesite esperar a que termine un hilo (por ejemplo, daemons, que deben ejecutarse hasta la finalización del proceso):

  1. Para liberar el controlador lateral de la aplicación: uno puede permitir que un objeto std::thread salga del ámbito sin unirse, lo que normalmente lleva a una llamada a std::terminate() sobre la destrucción.
  2. Permitir que el sistema operativo limpie los recursos específicos de la TCB ( TCB ) automáticamente tan pronto como finalice la secuencia, porque especificamos explícitamente que no estamos interesados ​​en unir la secuencia más adelante, por lo tanto, no se puede unir una hebra ya separada.

Killing Threads

El comportamiento en la terminación del proceso es el mismo que el del hilo principal, que al menos podría captar algunas señales. Si otros subprocesos pueden manejar señales no es tan importante, ya que uno podría unirse o terminar otros subprocesos dentro de la invocación del manejador de señal del subproceso principal. (Pregunta relacionada )

Como ya se dijo, cualquier hilo, ya sea separado o no, morirá con su proceso en la mayoría de los sistemas operativos . El proceso en sí puede terminarse elevando una señal, llamando a exit() o regresando de la función principal. Sin embargo, C ++ 11 no puede y no trata de definir el comportamiento exacto del sistema operativo subyacente, mientras que los desarrolladores de una máquina virtual Java seguramente pueden abstraer tales diferencias hasta cierto punto. Los modelos AFAIK, de procesos exóticos y de subprocesos generalmente se encuentran en plataformas antiguas (a las que probablemente C ++ 11 no se transferirá) y varios sistemas integrados, que podrían tener una implementación de biblioteca de idiomas especial y / o limitada y también soporte de lenguaje limitado.

Subproceso de soporte

Si los hilos no son compatibles, std::thread::get_id() debería devolver un id no válido ( std::thread::id predeterminado) ya que hay un proceso simple, que no necesita un objeto de hilo para ejecutarse y el constructor de un std::thread debería arrojar un std::system_error . Así es como entiendo C ++ 11 junto con los SO de hoy. Si hay un sistema operativo con soporte para subprocesos, que no genera un hilo principal en sus procesos, avíseme.

Control de hilos

Si uno necesita mantener el control sobre un hilo para el apagado correcto, uno puede hacer eso usando primitivas de sincronización y / o algún tipo de banderas. Sin embargo, en este caso, la configuración de un indicador de apagado seguido de una combinación es la que yo prefiero, ya que no tiene sentido aumentar la complejidad separando subprocesos, ya que los recursos se liberarían al mismo tiempo de todos modos, donde los pocos bytes del std::thread objeto de std::thread frente a una mayor complejidad y posiblemente más primitivas de sincronización deberían ser aceptables.

Supongamos que estoy iniciando un std::thread y luego se lo detach() , por lo que el hilo continúa ejecutándose a pesar de que el std::thread que una vez lo representó, sale del alcance.

Supongamos además que el programa no tiene un protocolo confiable para unir el subproceso separado 1 , por lo que el subproceso desconectado aún se ejecuta cuando sale main() .

No puedo encontrar nada en el estándar (más precisamente, en el borrador N3797 C ++ 14), que describe lo que debería suceder, ni 1.10 ni 30.3 contienen una redacción pertinente.

1 Otra pregunta, probablemente equivalente, es: "¿se puede volver a unir un hilo separado?", Porque cualquier protocolo que invente unirse, la parte de señalización tendría que hacerse mientras el hilo todavía se estaba ejecutando, y el programador del sistema operativo podría Decidir poner el hilo a dormir durante una hora justo después de que se realizó la señalización, sin que el extremo receptor pueda detectar de manera fiable que el hilo realmente terminó.

Si quedarse sin main() con subprocesos desconectados en ejecución es un comportamiento indefinido, cualquier uso de std::thread::detach() es un comportamiento indefinido a menos que el subproceso principal nunca salga 2 .

Por lo tanto, quedarse sin main() con subprocesos desconectados en ejecución debe tener efectos definidos . La pregunta es: dónde (en el estándar de C ++ , no en POSIX, no en los documentos del SO, ...) se definen esos efectos.

2 Un hilo separado no se puede unir (en el sentido de std::thread::join() ). Puede esperar los resultados de los subprocesos desconectados (por ejemplo, a través de un futuro desde std::packaged_task , o mediante un semáforo de conteo o un indicador y una variable de condición), pero eso no garantiza que el subproceso haya terminado de ejecutarse . De hecho, a menos que coloque la parte de señalización en el destructor del primer objeto automático del subproceso, habrá , en general, un código (destructores) que se ejecutará después del código de señalización. Si el SO planifica el hilo principal para consumir el resultado y salir antes de que el hilo separado termine de ejecutar dichos destructores, ¿qué será lo que Wis definirá?


Considera el siguiente código:

#include <iostream> #include <string> #include <thread> #include <chrono> void thread_fn() { std::this_thread::sleep_for (std::chrono::seconds(1)); std::cout << "Inside thread function/n"; } int main() { std::thread t1(thread_fn); t1.detach(); return 0; }

Al ejecutarlo en un sistema Linux, el mensaje de thread_fn nunca se imprime. El sistema operativo realmente limpia thread_fn() tan pronto como sale main() . Reemplazar t1.detach() con t1.join() siempre imprime el mensaje como se esperaba.


Cuando finaliza el hilo principal (es decir, el hilo que ejecuta la función main ()), el proceso finaliza y todos los demás hilos se detienen.

Referencia: https://.com/a/4667273/2194843


El destino del hilo después de que el programa sale es un comportamiento indefinido. pero un sistema operativo moderno limpiará todos los hilos creados por el proceso al cerrarlo.

Al separar un std::thread , estas tres condiciones continuarán reteniendo

  1. *this ya no posee ningún hilo
  2. joinable () siempre será igual a falso
  3. get_id () será igual a std :: thread :: id ()

La respuesta a la pregunta original "qué pasa con un hilo separado cuando main() sale" es:

Continúa ejecutándose (porque el estándar no dice que está parado), y eso está bien definido, siempre que no toque ninguna variable (automática | hilo_local) de otros hilos ni objetos estáticos.

Esto parece estar permitido para permitir los gestores de subprocesos como objetos estáticos (nota en [basic.start.term] / 4 dice tanto, gracias a @dyp para el puntero).

Los problemas surgen cuando la destrucción de los objetos estáticos ha terminado, porque entonces la ejecución ingresa a un régimen donde solo el código permitido en manejadores de señal puede ejecutarse ( [basic.start.term] / 1, 1ra oración ). De la biblioteca estándar de C ++, esa es solo la biblioteca <atomic> ( [support.runtime] / 9, 2nd sentence ). En particular, eso, en general, excluye condition_variable (su implementación define si se guarda para usar en un manejador de señal, porque no es parte de <atomic> ).

A menos que haya desenrollado su pila en este punto, es difícil ver cómo evitar el comportamiento indefinido.

La respuesta a la segunda pregunta "¿pueden volverse a unir los hilos separados?" Es:

Sí, con la familia de funciones *_at_thread_exit ( notify_all_at_thread_exit() , std::promise::set_value_at_thread_exit() , ...).

Como se señala en la nota al pie [2] de la pregunta, señalar una variable de condición o un semáforo o un contador atómico no es suficiente para unir un hilo aislado (en el sentido de garantizar que el final de su ejecución ha sucedido antes de la recepción de dicha señalización por un hilo de espera), porque, en general, habrá más código ejecutado después de, por ejemplo, un notify_all() de una variable de condición, en particular los destructores de objetos automáticos y locales de subprocesos.

Ejecutar la señalización como la última cosa que hace el hilo ( después de que los destructores de objetos automáticos y locales de subprocesos ocurrieron ) es para lo que se _at_thread_exit familia de funciones _at_thread_exit .

Por lo tanto, para evitar comportamientos indefinidos en ausencia de garantías de implementación superiores a las requeridas por el estándar, necesita (manualmente) unir un hilo separado con una función _at_thread_exit haciendo la señalización o hacer que el hilo separado ejecute solo el código que sería seguro para un controlador de señal, también.