Fin de thread, comment faire?

Bonjour,

Je poste un topic juste pour vous demander votre façon de gérer les threads (mais en particulier: la façon dont vous les terminer.

Voici une petit application (en faite c’est un serveur HTTP) mais j’ai enlevé ce qui m’intéréssait pas puisque ce qui m’interesse dans ce cas là c’est les threads.


int main (int argc, char* argv[])
{
	
     //Lancement d'un thread afin de faire des opérations bloquante en toile de fond sans 
     // bloquer l'utilisateur
     hThreadEcoute = CreateThread(NULL, 0, ThreadServerProc, NULL, CREATE_SUSPENDED, NULL);	

     //Ferme le handle du thread directement (plus aucune gestion sur lui)
     CloseHandle(hThreadEcoute);

     //Attend une demande de fin de programme par l'utilisateur
     getchar();
}

DWORD WINAPI ThreadClientProc(LPVOID param)
{
     /* Ici des opération non-bloquantes sont en cours pour répondre au client
        mais comme ces opérations sont longues je les ai mises dans un thread
     */

     ExitThread(0);
}

//Procédure principale du serveur
DWORD WINAPI ThreadServerProc(LPVOID)
{
     while( 1 )
     {
          /* ici il y a une opération bloquante */
  
          //Création d'un thread afin de répondre à une demande
          HANDLE hThreadClient = CreateThread(NULL, 0, ThreadClientProc, NULL, 0, 0);

          CloseHandle(hThreadClient);
     }

}

Ma question est la suivante

Le thread principal ne devrait jamais s’arrêter, mais l’utiliseur peut bien utilier le “Ctrl + C” du clavier pour stopper le programme (ou tout simplement lorsque l’ordinateur s’éteint).
Es-ce grave si il n’y pas de ExitThread, donc que le thread ne se termine pas convenablement?

Faudrait-t-il que dans ce cas là je rende mes sockets non bloquant et que je test avec une variable si la fin du thread est demandée?

Exemple:


BOOL bRunning; //Variable gloable

while( bRunning )
{
     /* ici il y a une opération NON bloquante */
  
     //Création d'un thread afin de répondre à une demande
     HANDLE hThreadClient = CreateThread(NULL, 0, ThreadClientProc, NULL, 0, 0);

     CloseHandle(hThreadClient);
}

et dans la procédure principale, lorsque l’utilisateur le demande, je mets bRunning à “false”, j’attends que le thread se termine, et si il se termine pas je le supprime de la façon moche (TerminateThread).


hThreadEcoute = CreateThread(NULL, 0, ThreadServerProc, NULL, CREATE_SUSPENDED, NULL);	

getchar();

if( WaitForSingleObject(hThreadEcoute, 200) == WAIT_TIMEOUT )
{
     TerminateThread(hThreadEcoute, -1);
}

CloseHandle(hThreadEcoute);

PS: Dans l’exemple je n’ai pas mis de sémaphore pour la variable globale, mais il est clair que j’en mettrais dans mon programme final.

Perso, j’utilise la technique du flag, j’utilise en plus un second flag pour m’assurer de ne pas tomber sur une boucle infinie, ce qui donnerais :


BOOL bThreadRunning;
BOOL hKillTreadRequired;

DWORD WINAPI ThreadClientProc(LPVOID param)
{
    /* Ici des opération non-bloquantes sont en cours pour répondre au client
       mais comme ces opérations sont longues je les ai mises dans un thread
    */
    if hKillTreadRequired
       bThreadRunning = false;

    ExitThread(0);
}

while( bThreadRunning )
{
    /* ici il y a une opération NON bloquante */
 
    //Création d'un thread afin de répondre à une demande
    HANDLE hThreadClient = CreateThread(NULL, 0, ThreadClientProc, NULL, 0, 0);

    CloseHandle(hThreadClient);
}


hThreadEcoute = CreateThread(NULL, 0, ThreadServerProc, NULL, CREATE_SUSPENDED, NULL);

getchar();

// dans la fonction de fermeture du thread
while (bThreadRunning)
{
	bThreadKillRequired = true;
}
if (m_hThread != INVALID_HANDLE_VALUE) CloseHandle(m_hThread);

Arf, un peu de la peine moi ce soir. Je vais essayer de comprendre le code demain :stuck_out_tongue:
Mais ne crois tu pas que ta boucle est un peu mauvaise pour les ressources?

Je ferais plutôt:

// dans la fonction de fermeture du thread
while (bThreadRunning)
{
    Sleep(200);
    bThreadKillRequired = true;
}

Mais ceci pose alors un problème, ces "Sleep" dans un programme retarde sa fin si on met le flags "bRunning" à false…
Ne serait-ce pas un peu grave? En particulier pour un utilisateur qui a le dernier ordinateur du marché et qui exige que le programme réponde au quart de tour :sweet: ?

J’ai une petite question pour toi : Ctrl C c’est pas censé envoyer un signal?

Et dans ce cas, tu n’as pas moyen avec la winapi (que je connais pas) de récupérer le signal pour proprement quitter?

J’utilise cette méthode depuis un moment et c’est suite à une boucle infinie que j’ai ajouté ce deuxième flag.
Au début, j’utilisais moi aussi WaitForSingleObject mais bon, ma méthode marche sans, je fait l’économie d’une API.

Je ne peux en effet que plussoyer :ane:
==> je viens de trouver ça ===> SetConsoleCtrlHandler

C’est bien ce que je pensais :slight_smile:

Et deuxième petite question, si des milliers de clients se connectent sur mon serveur HTTP (bien sûr ça peu arriver! :paf: ) ==> il y a des centaines de threads qui vont se créer et se lancer en même temps ==> mauvais pour les ressources CPUs :p.

Faudrait-t-il un contrôle sur le nb de threads et bloquer à un certain moment?

Autre question, je n’ai aucun contrôle sur les threads pour répondre au client (j’ai fermé le handle directement), d’après vous faudrait-il que je fasse un tableau de HANDLE qui permettrait d’attendre que tous les threads soient fini avant de terminer le programme?

Ou le fait que un thread tourne encore quand le processus n’est pas trop grave?

Pourquoi ne pas utiliser les events (SetEvent, tout ça) pour que le client signal au serveur qu’il va s’arrêter ?

Dusty> sur le protocole HTTP, 1.1 en tout cas, tu as la notion de Keep Alive: c’est à dire que si le client se reconnecte, on réutilise la connexion précédente plutôt que de la rouvrir (ça coûte cher).