¿Cómo termino un hilo en C ++ 11?

Respuestas:

138
  1. Puede llamar std::terminate()desde cualquier hilo y el hilo al que se refiere terminará forzosamente.

  2. Puede hacer arreglos para ~thread()que se ejecute en el objeto del hilo objetivo, sin una intervención join()ni detach()en ese objeto. Esto tendrá el mismo efecto que la opción 1.

  3. Podría diseñar una excepción que tenga un destructor que arroje una excepción. Y luego haga arreglos para que el hilo objetivo arroje esta excepción cuando se va a terminar con fuerza. La parte difícil de este es conseguir que el hilo objetivo arroje esta excepción.

Las opciones 1 y 2 no pierden recursos dentro del proceso, pero terminan cada hilo.

La opción 3 probablemente perderá recursos, pero es parcialmente cooperativa en que el hilo objetivo debe aceptar lanzar la excepción.

No hay una forma portátil en C ++ 11 (que yo sepa) de matar de forma no cooperativa un solo hilo en un programa de múltiples hilos (es decir, sin matar todos los hilos). No hubo motivación para diseñar tal característica.

A std::threadpuede tener esta función miembro:

native_handle_type native_handle();

Es posible que pueda usar esto para llamar a una función dependiente del sistema operativo para hacer lo que desee. Por ejemplo, en los sistemas operativos de Apple, esta función existe y native_handle_typees a pthread_t. Si tiene éxito, es probable que pierda recursos.

Howard Hinnant
fuente
2
Un pequeño detalle sobre "no pierda recursos dentro del proceso" : si bien es cierto que el sistema operativo reclamará todos los recursos después de finalizar el proceso, los recursos se filtran en lo que respecta al programa. Eso suele ser irrelevante, pero aún puede ser un problema en algunos casos. std::terminateni llama a los destructores estáticos ni vacía los búferes de salida, por lo que el orden en que se liberan los recursos no está bien definido, ni tiene ninguna garantía de que alguno de sus datos sea visible para el usuario o escrito en la tienda permanente, o incluso consistente y completo.
Damon
66
También puede llamar exit()o abort()al mismo efecto general.
n. 'pronombres' m.
2
# 1 es una broma y @ChrisDodd tiene razón. El chiste se explica en la respuesta en la primera oración debajo del # 3. También vea la respuesta de Nanno Langstraat y los comentarios debajo.
Howard Hinnant
43

La respuesta de @Howard Hinnant es correcta y completa. Pero podría malinterpretarse si se lee demasiado rápido, porque std::terminate()(todo el proceso) tiene el mismo nombre que la "terminación" que @AlexanderVX tenía en mente (1 hilo).

Resumen: "terminar 1 hilo + con fuerza (el hilo objetivo no coopera) + puro C ++ 11 = De ninguna manera".

Nanno Langstraat
fuente
14
Ahh, mi # 1 es realmente divertido. No tiene que especificar qué subproceso desea finalizar forzosamente. ¡El sistema simplemente sabe mágicamente cuál quieres terminar con fuerza y ​​lo hace!
Howard Hinnant
8
Sí, la std::terminate()respuesta es como una clásica historia traviesa de Djinn; cumple con todo lo que el OP desea al pie de la letra, aunque probablemente no de la manera que él quiso decir . El humor discreto me hizo sonreír. :-)
Nanno Langstraat
2
Solo tengo que evitar que los principiantes inocentes de C ++ se ilusionen demasiado o por mucho tiempo.
Nanno Langstraat
2
Elegantemente declarado. :-)
Howard Hinnant
@NannoLangstraat dammnit ... Tenía muchas esperanzas, hasta que seguí leyendo: (..lol, oh bueno + 1's por todas partes)
code_fodder
13

Esta pregunta en realidad tiene una naturaleza más profunda y una buena comprensión de los conceptos de subprocesos múltiples en general le proporcionará información sobre este tema. De hecho, no hay ningún idioma o sistema operativo que le brinde facilidades para la terminación asincrónica de subprocesos bruscamente sin advertencia de no usarlos. Y todos estos entornos de ejecución aconsejan encarecidamente al desarrollador o incluso requieren construir aplicaciones de subprocesos múltiples sobre la base de terminación de subprocesos cooperativa o síncrona. La razón de estas decisiones y consejos comunes es que todos se basan en el mismo modelo general de subprocesos múltiples.

Comparemos los conceptos de multiprocesamiento y subprocesamiento múltiple para comprender mejor las ventajas y limitaciones del segundo.

El multiprocesamiento supone la división de todo el entorno de ejecución en un conjunto de procesos completamente aislados controlados por el sistema operativo. El proceso incorpora y aísla el estado del entorno de ejecución, incluida la memoria local del proceso y los datos dentro de él y todos los recursos del sistema como archivos, sockets, objetos de sincronización. El aislamiento es una característica críticamente importante del proceso, ya que limita la propagación de fallas por las fronteras del proceso. En otras palabras, ningún proceso puede afectar la consistencia de cualquier otro proceso en el sistema. Lo mismo es cierto para el comportamiento del proceso, pero en la forma menos restringida y más borrosa. En dicho entorno, cualquier proceso puede ser eliminado en cualquier momento "arbitrario", porque en primer lugar cada proceso está aislado, en segundo lugar,

En contraste, el subproceso múltiple supone ejecutar múltiples subprocesos en el mismo proceso. Pero todos estos hilos comparten el mismo cuadro de aislamiento y no hay ningún control del sistema operativo sobre el estado interno del proceso. Como resultado, cualquier hilo puede cambiar el estado del proceso global, así como corromperlo. Al mismo tiempo, los puntos en los que se sabe que el estado del subproceso es seguro para matar un subproceso depende completamente de la lógica de la aplicación y no se conocen ni para el sistema operativo ni para el tiempo de ejecución del lenguaje de programación. Como resultado, la terminación de subprocesos en el momento arbitrario significa matarlo en el punto arbitrario de su ruta de ejecución y puede conducir fácilmente a la corrupción de datos, memoria y manejos de todo el proceso,

Debido a esto, el enfoque común es obligar a los desarrolladores a implementar la terminación de subprocesos sincrónica o cooperativa, donde un subproceso puede solicitar otra terminación de subprocesos y otro subproceso en un punto bien definido puede verificar esta solicitud e iniciar el procedimiento de apagado desde el estado bien definido con la liberación de todos los recursos globales de todo el sistema y los recursos locales de todo el proceso de manera segura y consistente.

ZarathustrA
fuente
8
Esta no es una respuesta muy útil. El procesamiento múltiple no es relevante aquí, y las observaciones sobre el subprocesamiento múltiple son bastante amplias.
MSalters
44
Lo que quería decir y decir en detalle es que el modelo de subprocesos múltiples no proporciona una forma formal de terminación de subprocesos enérgica. C ++ quiere seguir los modelos claros, incluidos el modelo de memoria, el modelo de subprocesos múltiples, etc. El método de terminación de subprocesos forzado es inherentemente inseguro. Si el comité estándar de C ++ se viera obligado a agregarlo a C ++, se haría con la siguiente instrucción "Método terminar () finaliza la ejecución del hilo. Comportamiento indefinido", que sonará como "hacer algo de magia y (posiblemente) termina el hilo ".
ZarathustrA
C # tiene esta característica.
Derf Skren
@Derf Skren "De hecho, no hay ningún idioma o sistema operativo que le brinde facilidades para la terminación asincrónica de subprocesos bruscamente sin advertencia de no usarlos" (c) ZarathustrA "Observaciones: ¡Importante! El método Thread.Abort debe usarse con precaución En particular, cuando lo llama para abortar un hilo que no sea el hilo actual, no sabe qué código se ha ejecutado o no se pudo ejecutar ... "(c) Enlace de Microsoft: docs.microsoft.com/en-us/dotnet/ api / ...
ZarathustrA
11

Consejos para usar la función dependiente del sistema operativo para terminar el hilo de C ++:

  1. std::thread::native_handle()solo puede obtener el tipo de identificador nativo válido del hilo antes de llamar join()o detach(). Después de eso, native_handle()devuelve 0 - se pthread_cancel()reducirá.

  2. Para llamar efectivamente a la función de terminación de subprocesos nativos (por ejemplo pthread_cancel()), debe guardar el identificador nativo antes de llamar std::thread::join()o std::thread::detach(). Para que su terminador nativo siempre tenga un identificador nativo válido para usar.

Más explicaciones, consulte: http://bo-yang.github.io/2017/11/19/cpp-kill-detached-thread .

boyang
fuente
5

Supongo que el hilo que necesita ser eliminado está en cualquier tipo de modo de espera o está haciendo un trabajo pesado. Sugeriría usar una forma "ingenua".

Definir algunos booleanos globales:

std::atomic_bool stop_thread_1 = false;

Ponga el siguiente código (o similar) en varios puntos clave, de manera que haga que todas las funciones en la pila de llamadas regresen hasta que el hilo termine naturalmente:

if (stop_thread_1)
    return;

Luego, para detener el hilo de otro hilo (principal):

stop_thread_1 = true;
thread1.join ();
stop_thread_1 = false; //(for next time. this can be when starting the thread instead)
Doron Ben-Ari
fuente