Comment mettre fin à un thread en C ++ 11?

137

Je n'ai pas besoin de terminer correctement le thread ou de le faire répondre à une commande "terminer". Je suis intéressé à terminer le thread avec force en utilisant du C ++ 11 pur.

Alexandre V
la source
7
Voici une bonne question sur ce sujet: stackoverflow.com/questions/2790346/c0x-thread-interruption "Toutes les spécifications du langage indiquent que le support n'est pas intégré au langage"
Nemanja Boric

Réponses:

138
  1. Vous pouvez appeler à std::terminate()partir de n'importe quel thread et le thread auquel vous faites référence se terminera de force.

  2. Vous pouvez faire en sorte ~thread()d'être exécuté sur l'objet du thread cible, sans intervention join()ni detach()sur cet objet. Cela aura le même effet que l'option 1.

  3. Vous pouvez concevoir une exception qui a un destructeur qui lève une exception. Et puis faites en sorte que le thread cible lève cette exception lorsqu'il doit être arrêté de force. La partie délicate de celui-ci est d'amener le thread cible à lever cette exception.

Les options 1 et 2 ne fuient pas les ressources intra-processus, mais elles terminent chaque thread.

L'option 3 entraînera probablement une fuite de ressources, mais elle est partiellement coopérative en ce que le thread cible doit accepter de lever l'exception.

Il n'y a pas de moyen portable en C ++ 11 (à ma connaissance) de tuer de manière non coopérative un seul thread dans un programme multi-thread (c'est-à-dire sans tuer tous les threads). Il n'y avait aucune motivation pour concevoir une telle fonctionnalité.

A std::threadpeut avoir cette fonction membre:

native_handle_type native_handle();

Vous pourrez peut-être l'utiliser pour appeler une fonction dépendante du système d'exploitation pour faire ce que vous voulez. Par exemple sur les OS d'Apple, cette fonction existe et native_handle_typeest un pthread_t. Si vous réussissez, vous risquez de perdre des ressources.

Howard Hinnant
la source
2
Léger pinaillage sur «ne pas fuir les ressources intra-processus» : s'il est bien sûr vrai que le système d'exploitation récupérera toutes les ressources après avoir tué le processus, des ressources sont divulguées en ce qui concerne le programme. Ce n'est généralement pas pertinent, mais cela peut encore poser problème dans certains cas. std::terminaten'appelle ni les destructeurs statiques ni ne vide les tampons de sortie, donc l'ordre dans lequel les ressources sont libérées n'est pas bien défini, et vous n'avez aucune garantie que l'une de vos données est visible par l'utilisateur ou écrite dans le magasin permanent, ou même cohérent et complet.
Damon
6
Vous pouvez également appeler exit()ou abort()au même effet global.
n. «pronoms» m.
2
# 1 est une blague et @ChrisDodd a raison. La blague est expliquée dans la réponse dans la première phrase sous # 3. Voir également la réponse de Nanno Langstraat et les commentaires ci-dessous.
Howard Hinnant
43

La réponse de @Howard Hinnant est à la fois correcte et complète. Mais il pourrait être mal compris s'il est lu trop rapidement, parce que std::terminate()(tout le processus) a le même nom que le "terminateur" que @AlexanderVX avait en tête (1 thread).

Résumé: "terminer 1 thread + avec force (le thread cible ne coopère pas) + C ++ 11 pur = pas moyen."

Nanno Langstraat
la source
14
Ahh, mon n ° 1 est vraiment drôle. Vous n'êtes pas obligé de spécifier le fil de discussion auquel vous souhaitez forcer la fin. Le système sait comme par magie lequel vous voulez mettre fin avec force et le fait!
Howard Hinnant
8
Oui, la std::terminate()réponse est comme une histoire malicieuse classique de Djinn; il accomplit tout ce que souhaite le PO à la lettre, mais probablement pas de la manière qu'il voulait dire . L'humour discret m'a fait sourire. :-)
Nanno Langstraat
2
Il suffit d'empêcher les novices innocents du C ++ de faire trop espérer.
Nanno Langstraat
2
Élégamment déclaré. :-)
Howard Hinnant
@NannoLangstraat dammnit ... J'avais de grands espoirs, jusqu'à ce que je continue à lire: (..lol, eh bien + 1 est tout autour
:)
13

Cette question a en fait une nature plus profonde et une bonne compréhension des concepts multithreading en général vous donnera un aperçu de ce sujet. En fait, il n'y a pas de langage ni de système d'exploitation qui vous offrent des possibilités de terminaison de threads asynchrone brutalement sans avertissement de ne pas les utiliser. Et tous ces environnements d'exécution conseillent fortement les développeurs ou même nécessitent de créer des applications multithreading sur la base d'une terminaison de thread coopérative ou synchrone. La raison de ces décisions et conseils communs est qu'ils sont tous construits sur la base du même modèle multithreading général.

Comparons les concepts de multitraitement et de multithreading pour mieux comprendre les avantages et les limites du second.

Le multitraitement suppose la division de l'ensemble de l'environnement d'exécution en un ensemble de processus complètement isolés contrôlés par le système d'exploitation. Process incorpore et isole l'état de l'environnement d'exécution, y compris la mémoire locale du processus et les données qu'il contient, ainsi que toutes les ressources système telles que les fichiers, les sockets et les objets de synchronisation. L'isolement est une caractéristique extrêmement importante du processus, car elle limite la propagation des défauts par les frontières du processus. En d'autres termes, aucun processus ne peut affecter la cohérence d'un autre processus dans le système. Il en va de même pour le comportement du processus mais de manière moins restreinte et plus floue. Dans un tel environnement, n'importe quel processus peut être tué à n'importe quel moment "arbitraire", car premièrement chaque processus est isolé, deuxièmement

En revanche, le multithreading suppose l'exécution de plusieurs threads dans le même processus. Mais tous ces threads partagent la même boîte d'isolation et il n'y a aucun contrôle du système d'exploitation sur l'état interne du processus. En conséquence, n'importe quel thread est capable de modifier l'état global du processus et de le corrompre. Au même moment, les points dans lesquels l'état du thread est bien connu pour être sûr de tuer un thread dépendent complètement de la logique de l'application et ne sont connus ni pour le système d'exploitation ni pour l'exécution du langage de programmation. En conséquence, l'arrêt du thread à un moment arbitraire signifie le tuer à un point arbitraire de son chemin d'exécution et peut facilement conduire à la corruption des données, à la mémoire et aux fuites de traitement à l'échelle du processus,

Pour cette raison, l'approche courante consiste à forcer les développeurs à implémenter la terminaison de thread synchrone ou coopérative, où l'un des threads peut demander une autre terminaison de thread et l'autre thread dans un point bien défini peut vérifier cette demande et démarrer la procédure d'arrêt à partir de l'état bien défini. avec la libération de toutes les ressources à l'échelle du système mondial et des ressources locales à l'échelle du processus de manière sûre et cohérente.

ZarathustrA
la source
8
Ce n'est pas une réponse très utile. Le multi-traitement n'est pas pertinent ici, et les observations sur le multi-threading sont plutôt larges.
MSalters
4
Ce que je voulais dire et dire en détail, c'est que le modèle multithreading ne fournit pas de moyen formel de résiliation forcée des threads. C ++ veut suivre les modèles clairs, y compris le modèle de mémoire, le modèle multithreading, etc. La méthode d'arrêt forcé des threads est intrinsèquement dangereuse. Si le comité standard C ++ était obligé de l'ajouter à C ++, cela serait fait avec l'instruction suivante "La méthode terminate () met fin à l'exécution du thread. Comportement non défini.", Qui ressemblera à "make some magic and (éventuellement) termine le thread ".
ZarathustrA
C # a cette fonctionnalité.
Derf Skren le
@Derf Skren "En fait, il n'y a pas de langage ou de système d'exploitation qui vous offre des facilités pour la terminaison de threads asynchrone brusquement sans avertissement pour ne pas les utiliser" (c) ZarathustrA "Remarques: Important! La méthode Thread.Abort doit être utilisée avec prudence . En particulier lorsque vous l'appelez pour abandonner un thread autre que le thread actuel, vous ne savez pas quel code a été exécuté ou n'a pas pu s'exécuter ... "(c) Lien Microsoft: docs.microsoft.com/en-us/dotnet/ api /…
ZarathustrA
11

Conseils d'utilisation de la fonction dépendante du système d'exploitation pour terminer le thread C ++:

  1. std::thread::native_handle()peut uniquement obtenir le type de handle natif valide du thread avant d'appeler join()ou detach(). Après cela, native_handle()renvoie 0 - pthread_cancel()coredump.

  2. Pour appeler efficacement la fonction de terminaison de thread natif (par exemple pthread_cancel()), vous devez enregistrer le handle natif avant d'appeler std::thread::join()ou std::thread::detach(). Pour que votre terminateur natif ait toujours un handle natif valide à utiliser.

Pour plus d'explications, veuillez consulter: http://bo-yang.github.io/2017/11/19/cpp-kill-detached-thread .

boyang
la source
6

Je suppose que le fil qui doit être tué est soit dans n'importe quel mode d'attente, soit en effectuant un travail difficile. Je suggérerais d'utiliser une manière «naïve».

Définissez un booléen global:

std::atomic_bool stop_thread_1 = false;

Placez le code suivant (ou similaire) en plusieurs points clés, de manière à ce que toutes les fonctions de la pile d'appels soient renvoyées jusqu'à ce que le thread se termine naturellement:

if (stop_thread_1)
    return;

Ensuite, pour arrêter le thread d'un autre thread (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
la source