8 votes

Arrêter le fil en toute sécurité

J'écris un programme à plusieurs fils.

Je voudrais savoir quelle est la différence entre TerminateThread y ExitThread ?

Voici mon extrait de code lorsque WM_DESTROY reçu :

void CleanAll()
{
    DWORD dwExit[MAX_THREAD];
    for(int i = 0; i < MAX_THREAD; i++)
    {
        GetExitCodeThread(hThread[i], &dwExit[i]);
        // I used ExitThread(dwExit[i]); previously
        TerminateThread(hThread[i], dwExit[i]);
        CloseHandle(hThread[i]);
    }
}

J'ai utilisé ExitThread() auparavant, mais mon programme stikk dans le Gestionnaire des tâches, alors je le change en TerminateThread() et mon programme a disparu du gestionnaire des tâches.

Toute explication préalable est très appréciée.

12voto

selbie Points 20267

TerminateThread forces un autre fil pour sortir. Vous devez éviter à tout prix de l'appeler, car elle arrêtera net un thread sans qu'il puisse être nettoyé. Cela inclut toute mémoire CRT allouée.

ExitThread est pour le fil de discussion en cours pour s'arrêter proprement et gentiment. Lorsque vous l'avez appelé ci-dessus, vous avez probablement forcé le thread principal (UI) à sortir et vous avez probablement laissé les threads en cours d'exécution traîner dans les parages. Par conséquent, votre programme était toujours en cours d'exécution, comme le montre le gestionnaire de tâches. GetExitCodeThread a probablement échoué puisque les threads n'étaient pas encore sortis.

Mais la bonne façon d'arrêter un thread est de lui signaler proprement, par tous les moyens propres nécessaires, qu'il doit se terminer. Laissez ensuite les fils se terminer d'eux-mêmes. avant d'autoriser la sortie du thread principal. Dans l'exemple suivant, j'ai utilisé un drapeau global pour indiquer aux threads qu'ils doivent quitter. Mais cela suppose que vos threads auront toujours la possibilité d'interroger l'état bool global. Une autre approche plus propre consiste à demander à chaque thread d'appeler WaitForSingleObject sur un handle d'événement. Lorsque la poignée d'événement est signalée, le thread vérifie la variable globale et sort si nécessaire.

bool global_Need_ToExit;  // use a bool or replace with an event handle the thread shoudl wait on

void CleanAll()
{
    //signal all threads to exit
    global_Need_ToExit = true;

    DWORD dwExit[MAX_THREAD];
    for(int i = 0; i < MAX_THREAD; i++)
    {
        // actually wait for the thread to exit
        WaitForSingleObject(hThread[i], WAIT_INFINITE);

        // get the thread's exit code (I'm not sure why you need it)
        GetExitCodeThread(hThread[i], &dwExit[i]);

        // cleanup the thread
        CloseHandle(hThread[i]);
        hThread[i] = NULL;
    }
}

DWORD __stdcall YourThreadFunction(void* pData)
{

    while (global_Need_To_Exit == false)
    {
        // do more work
    }

    return 0; // same as ExitThread(0);
}

4voto

Steven Tilly Points 41

Désolé, mais la réponse la plus votée dit qu'il faut utiliser ExitThread parce qu'il "s'arrêtera tout seul et proprement". Ce n'est tout simplement pas vrai. La documentation indique : ExitThread est la méthode préférée pour quitter un thread en code C. Cependant, dans le code C++, le thread est quitté avant qu'un destructeur puisse être appelé ou qu'un autre nettoyage automatique puisse être effectué. Par conséquent, en code C++, vous devez retourner à partir de votre fonction de thread. Je l'ai appris à mes dépens en suivant le conseil d'un forum d'utiliser ExitThread et en passant des heures à me demander d'où venaient toutes mes fuites de mémoire. Oui, c'est pour le C++, mais c'est ce sur quoi porte cette question. Et en fait, même pour le C, il y a une mise en garde "Un thread dans un exécutable lié à la bibliothèque d'exécution C statique (CRT) doit utiliser _beginthread et _endthread pour la gestion des threads plutôt que CreateThread et ExitThread. Le non-respect de cette règle entraîne de petites fuites de mémoire lorsque le fil d'exécution appelle ExitThread". N'UTILISEZ PAS ExitThread sans être conscient des conséquences !

2voto

jweyrich Points 10002

Votre processus est-il censé se terminer lorsque votre fil est terminé ? Il peut y avoir de nombreuses explications au problème que vous avez décrit. Si vous voulez mettre fin à l'ensemble du processus, appelez simplement ExitProcess .

Le problème avec TerminateThread est très susceptible de provoquer une fuite de mémoire. Il ne se soucie pas de l'état du thread, ni des ressources qui lui sont allouées. Il peut également provoquer une impasse, en fonction de la manière dont vous procédez à la synchronisation. En d'autres termes, il ne met pas fin au thread de manière gracieuse.

La meilleure façon de mettre fin à un thread est de ne pas le terminer explicitement. N'appelez pas TerminateThread ni ExitThread mais juste return de la fonction du fil. Il se peut que vous deviez utiliser un drapeau atomique ou déclencher un événement (ou une autre méthode de synchronisation) pour signaler le moment où le thread doit se terminer. Votre thread doit alors vérifier périodiquement ce drapeau (ou cet événement) et désallouer toutes les ressources avant de revenir (se terminer).

0voto

Ibrahim Points 104

Vous pouvez utiliser SingleWaitObjects ou QueueUserAPC mais vous devez vous assurer que le thread s'arrête pour vérifier ces objets asynchrones ou en attendre un à la fin et terminer le thread normalement sous linux vous pouvez utiliser des signaux.

0voto

Zack Yezek Points 134

En fin de compte, vous devez considérer chaque fil de discussion comme un mini-processus (c'est ce qu'ils sont sous le capot de toute façon). Pour qu'il s'arrête proprement, vous devez mettre en place une infrastructure supplémentaire qui vous permette, à vous, le thread principal, de lui dire de s'arrêter.

La manière la plus simple de procéder est un système de signalisation, que les inventeurs de POSIX ont compris très tôt et qu'ils exposent donc directement dans le système d'exploitation. Le meilleur exemple réel d'un tel système est celui de la poste américaine. Chaque agent possède une adresse unique à partir de laquelle il peut envoyer et recevoir du courrier (signaux) provenant d'autres adresses, et c'est à l'agent de décider ce qu'il doit faire lorsqu'il reçoit un signal. Certains sont des informations ou du courrier indésirable et sont donc ignorés, tandis que d'autres sont hautement prioritaires et vous les ignorez à vos risques et périls.

Au niveau du code, vous avez besoin des éléments suivants pour que cela fonctionne bien (j'en ai construit quelques-uns pour des programmes de traitement d'images) :
1) Un objet abstrait "Event" ou "ControlSignal". N'hésitez pas à utiliser les événements Windows par défaut ou les signaux linux s'ils sont adéquats.
2) Une sorte de "tuyau" : Le système d'exploitation dispose souvent d'une primitive littérale du noyau APPELÉE "tuyau" qui permet à une extrémité de retirer des données tandis qu'une autre en insère. Ce type de pipe est généralement exagéré pour les communications au sein d'un processus, mais l'objet dont vous avez besoin est conceptuellement équivalent.
3) Code dans vos fils qui récupère les signaux du tuyau et agit en conséquence. 4) Un signal "stop" ou "annulation" que tous les threads reconnaissent.

Bien entendu, il est possible d'interrompre brutalement un thread avec TerminateThread, tout comme on peut utiliser taskkill ou CTRL-C pour tuer un processus. ExitThread est un moyen spécifique à Windows de construire un signal d'arrêt propre comme je le décris en (4), mais vous avez toujours besoin de la boucle while et de "WaitForSingleObject" (3) ainsi que de la poignée spécifique à Windows (2) pour que cela fonctionne.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X