IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Windows Discussion :

Coût de création et destruction de thread


Sujet :

Windows

  1. #1
    Membre éprouvé
    Inscrit en
    Avril 2005
    Messages
    1 110
    Détails du profil
    Informations forums :
    Inscription : Avril 2005
    Messages : 1 110
    Points : 937
    Points
    937
    Par défaut Coût de création et destruction de thread
    J'écris une application dont certaines parties peuvent bénéficer d'un traitement parallèle.

    En bref, il s'agit de rechercher des éléments dans un tableau, beaucoup d'éléments. Le tableau étant statique, je peux chercher 2 éléments en même temps par exemple.
    Dans une première approche, je me propose de créer un thread pour chaque recherche, mais comme la recherche en soi est rapide, je me demandais si je n'allais pas perdre plus de temps à créer/détruire le thread qu'à chercher.
    Ainsi, dans un deuxième temps, je me dis qu'il est mieux de créer 2 threads que l'on garde et réutilise pour chaque recherche. Mais je ne vois pas comment faire.
    Merci pour votre aide.

  2. #2
    Inactif  
    Avatar de Mac LAK
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    3 893
    Détails du profil
    Informations personnelles :
    Âge : 49
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Octobre 2004
    Messages : 3 893
    Points : 4 846
    Points
    4 846
    Par défaut
    La création/destruction de threads peut effectivement être un gouffre de performances assez infect.

    La solution : créer les threads, et déclencher leur exécution "à la demande".

    L'implémentation : Suspendre les threads (SuspendThread), mettre à jour leurs paramètres (le thread étant suspendu, pas de problèmes d'interblocage), redémarrer le thread (ResumeThread) : il effectue le traitement.
    A la fin, re-suspendre le thread, sachant qu'un thread peut parfaitement se suspendre lui-même ! ;-)

    En général, ça donne un truc de ce genre comme coeur de la fonction thread :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    while (FlagGeneral) {
      SuspendThread(GetCurrentThread());
      // Traitement parallèle.
      ....
      // Signalisation de la fin de traitement
      // On peut par exemple simplement incrémenter une variable globale.
      // On peut aussi déclencher un évènement, ou poster un message.
      InterlockedIncrement(UnAutreFlagGeneral);
    }
    Le principal problème, avec la programmation parallèle, c'est l'accès concurrent (ex : deux threads, y compris le thread principal, accédant en écriture à une variable simultanément).
    Mais si tu écris bien ton code, tu peux te retrouver aisément dans un cas où un seul thread du système accède en écriture, les autres ne faisant que "lire"... C'est un autre débat, cependant.

    Tu vois le principe ?
    Mac LAK.
    ___________________________________________________
    Ne prenez pas la vie trop au sérieux, de toutes façons, vous n'en sortirez pas vivant.

    Sources et composants Delphi sur mon site, L'antre du Lak.
    Pas de question technique par MP : posez-la dans un nouveau sujet, sur le forum adéquat.

    Rejoignez-nous sur : Serveur de fichiers [NAS] Le Tableau de bord projets Le groupe de travail ICMO

  3. #3
    Membre éprouvé
    Inscrit en
    Avril 2005
    Messages
    1 110
    Détails du profil
    Informations forums :
    Inscription : Avril 2005
    Messages : 1 110
    Points : 937
    Points
    937
    Par défaut
    Oui, tout à fait. Merci
    Voici ce que j'imagine suivant ton exemple
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    ThreadFunction()
    {
    do
    {
     //begin processing
     ...
     //end processing
     SuspendThread(GetCurrentThread());
    }
    while (1);//main thread will kill this thread, so don't worry infinite loop
    }
    A priori je n'ai pas besoin de plus que ça, suspendre le thread quand le processus est fini.

    Reste le thread principal, il faudrait qu'il "dorme" tant que les threads appelés ne sont pas suspendus. Je me vois mal faire une boucle qui testerait une variable globale comme tu le suggères avec InterlockedIncrement(UnAutreFlagGeneral). Car un simple code du genre
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    UnAutreFlagGeneral=0;
    InitAndStartThread();
    while (UnAutreFlagGeneral==0);
    consomme 100% de cpu pendant le while.
    Y'a-t-il une solution du genre?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    InitAndStartThread(hThread1);
    InitAndStartThread(hThread2);
    WaitUntilThreadSuspended(hThread1);//Win32 API call?
    WaitUntilThreadSuspended(hThread2);//Win32 API call?

  4. #4
    Inactif  
    Avatar de Mac LAK
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    3 893
    Détails du profil
    Informations personnelles :
    Âge : 49
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Octobre 2004
    Messages : 3 893
    Points : 4 846
    Points
    4 846
    Par défaut
    Citation Envoyé par camboui
    Voici ce que j'imagine suivant ton exemple
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    while (1);//main thread will kill this thread, so don't worry infinite loop
    Gni ??? Tu ne vas pas oser appeler TerminateThread, quand même, hum ??? Surtout que ton code, là, va bouffer 100% du temps CPU....

    Citation Envoyé par camboui
    A priori je n'ai pas besoin de plus que ça, suspendre le thread quand le processus est fini.
    C'est plutôt le contraire : suspendre le thread en attendant d'effectuer le processus... Bien sûr, il faut tester au réveil que ce n'est pas un réveil pour être détruit !
    L'idéal, c'est de passer par un évènement "privé" entre le thread principal et le thread de traitement.

    Citation Envoyé par camboui
    Reste le thread principal, il faudrait qu'il "dorme" tant que les threads appelés ne sont pas suspendus.
    Et pourquoi donc ? Si tu suspends ton thread principal, c'est que tu créé un thread de traitement de trop...
    Plus sérieusement, il doit rester actif, ne serait-ce qu'à cause de la boucle de messages.

    Citation Envoyé par camboui
    Je me vois mal faire une boucle qui testerait une variable globale comme tu le suggères avec InterlockedIncrement(UnAutreFlagGeneral).
    En effet : ce genre de "truc" n'est bon que pour la demande de terminaison du thread, et encore : il existe des cas où ça part en vrille.

    Citation Envoyé par camboui
    Y'a-t-il une solution du genre?
    Evènements privés. Par contre, comme je te l'ai dit, "endormir" ton thread principal montre une erreur de conception, car c'est non seulement peu recommandé, mais même carrément nuisible pour la boucle de message... Essaie de m'en dire plus, stp, on verra comment faire.
    Mac LAK.
    ___________________________________________________
    Ne prenez pas la vie trop au sérieux, de toutes façons, vous n'en sortirez pas vivant.

    Sources et composants Delphi sur mon site, L'antre du Lak.
    Pas de question technique par MP : posez-la dans un nouveau sujet, sur le forum adéquat.

    Rejoignez-nous sur : Serveur de fichiers [NAS] Le Tableau de bord projets Le groupe de travail ICMO

  5. #5
    Membre éprouvé
    Inscrit en
    Avril 2005
    Messages
    1 110
    Détails du profil
    Informations forums :
    Inscription : Avril 2005
    Messages : 1 110
    Points : 937
    Points
    937
    Par défaut
    En effet, je dois t'en dire plus car tu n'a pas compris mon besoin.

    C'est une application en mode console, du traitement de données. Aucune interaction donc avec un quelconque utilisateur. Clavier, souris, on s'en tape. Le programme sera même lancé de façon automatique via des triggers, scheduler, service, etc.

    Mon thread principal va lancer au moins 2 threads, bien sûr. Sinon c'est sans intérêt comme tu le soulignes. Mais il a besoin du résultat des ces 2 threads pour pouvoir continuer lui-même. Il ne fait donc rien pendant ce temps. Pour éviter d'écrire une boucle d'attente qui consomme du cpu, il faut l'endormir. Et j'ose espérer qu'il existe bien des fonctions API pour cela. En cherchant un peu, je crois que je dois utiliser des "mutex".
    Le thread principal créerait un mutex par thread, puis crée et lance les threads. Dès le départ ceux-ci ouvre les mutex. Le thread principal devrait alors attendre que les threads ferme les mutex.

    Voici un peu le genre:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    Thread(m)
    {
     do
     {
      h=OpenMutex(m);
      ...
      CloseHandle(h);
      SuspendThread();
     }
     while(1);
    }
    
    //main thread
    h1=CreateThread();
    h2=CreateThread();
    m1=CreateMutex();
    m2=CreateMutex();
    while (ThereIsData)
    {
     InitData(h1,m1,data1);
     InitData(h2,m2,data2);
     ResumeThread(h1);
     ResumeThread(h2);
     WaitForMutex(m1);
     WaitForMutex(m2);
     ...
     ProcessThreadResults();
     ...
     data1=data1->next;
     data2=data2->next;
     ThereIsData=data1.yes && data1.yes;
    }
    CloseHandle(m1);
    CloseHandle(m2);
    TerminateThread(h1);
    TerminateThread(h2);
    Le monde extérieur pour mon programme n'existe pas. Il prend des données, les traite, puis fournit d'autres données. C'est tout. Sauf que les calculs sont lourds et longs. J'ai besoin de faire ces calculs le plus rapidement possible en utilisant toutes les ressources disponibles.

    Voilà, j'espère que c'est plus clair
    Merci.

  6. #6
    Inactif  
    Avatar de Mac LAK
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    3 893
    Détails du profil
    Informations personnelles :
    Âge : 49
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Octobre 2004
    Messages : 3 893
    Points : 4 846
    Points
    4 846
    Par défaut
    Citation Envoyé par camboui
    En effet, je dois t'en dire plus car tu n'a pas compris mon besoin.
    A ma décharge, tu ne l'avais pas non plus précisemment défini... ;-)

    C'est plus clair, en effet.
    Plutôt que des mutex (qui sont nommés et donc existent sur l'intégralité du système), va jeter un oeil du côté des fonctions CreateEvent, SignalEvent et surtout, WaitForMultipleObjects !

    Avec tes deux attentes de mutex chaînées, tu risques surtout un interblocage assez mignon de tes threads... De même, des évènements te permettent de terminer tes threads proprement, sans surconsommation de temps CPU, et sans appeler TerminateThread (on ne doit JAMAIS appeler cette fonction, sauf dans des cas critiques d'erreur !!!).
    Pour ça, tu vas créer un event dans le thread principal, et le scanner dans le thread de traitement avec un "WaitForSingleObject(hEvent,0)" (tu noteras le timeout à zéro). Tant que le résultat de WaitForSingleObject sur cet évènement vaut WAIT_TIMEOUT, c'est que ton thread doit continuer à s'exécuter.
    Pour arrêter le thread, tu appelles SetEvent depuis le thread principal, et c'est tout... Ensuite, de nouveau un WaitForSingleObject, mais sur le handle du thread, pour attendre sa fermeture.

    Si tu as des problèmes pour les utiliser, demandes, OK ?
    Mac LAK.
    ___________________________________________________
    Ne prenez pas la vie trop au sérieux, de toutes façons, vous n'en sortirez pas vivant.

    Sources et composants Delphi sur mon site, L'antre du Lak.
    Pas de question technique par MP : posez-la dans un nouveau sujet, sur le forum adéquat.

    Rejoignez-nous sur : Serveur de fichiers [NAS] Le Tableau de bord projets Le groupe de travail ICMO

  7. #7
    Expert éminent sénior

    Homme Profil pro
    pdg
    Inscrit en
    Juin 2003
    Messages
    5 751
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : pdg

    Informations forums :
    Inscription : Juin 2003
    Messages : 5 751
    Points : 10 667
    Points
    10 667
    Billets dans le blog
    3
    Par défaut
    faut pas utiliser SuspendThread() / ResumeThread(), c'est à des fins de débogage uniquement. Il faut avoir certains privilèges.
    J'ai parcouru vite fait ton problème, pourquoi ton thread principal ne s'oocuperait-il pas de créer le mutex (qui peut être anonyme) / event qu'il file au thread au moment de sa création (paramètre lpParameter de CreateThread).
    Ton thread créé boucle sur
    WaitForSingleObject( hEventRecuEnParametre )
    et ton thread principal signale l'event pour débloquer le thread. Pas besoin de s'encombrer de OpenXXX..., le handle peut être filé directement d'un thread à l'autre.
    De même il faut prévoir un mécanisme qui demande au thread de s'arrêter totu seul proprement, car TerminateThread c'est aussi une mauvaise pratique (fuite de ressources...).

  8. #8
    Inactif  

    Homme Profil pro
    Ingénieur test de performance
    Inscrit en
    Décembre 2003
    Messages
    1 986
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur test de performance
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Décembre 2003
    Messages : 1 986
    Points : 2 605
    Points
    2 605
    Par défaut
    je me dis qu'il est mieux de créer 2 threads que l'on garde et réutilise pour chaque recherche.
    L'implémentation : Suspendre les threads (SuspendThread), mettre à jour leurs paramètres (le thread étant suspendu, pas de problèmes d'interblocage), redémarrer le thread (ResumeThread) :
    faut pas utiliser SuspendThread() / ResumeThread(), c'est à des fins de débogage uniquement. Il faut avoir certains privilèges.
    Question: comment fait-on pour créer un thread, le suspendre si besoin, le relancer, tout cela bien sur sans le détruire (sauf à la fin du programme).

    Du code s'il vous plaît.

    Voici le mien, est-il correct: (la gestion d'erreur et l'attente de fin de thread ont été volontairement omis).


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    #include <windows.h>
    
    #define THREAD_ACCESS &#40;THREAD_ALL_ACCESS | SYNCHRONIZE | THREAD_TERMINATE | THREAD_QUERY_INFORMATION&#41;
    
    DWORD WINAPI ThreadFct&#40;LPVOID&#41;&#123;
    
    		return 0;
    &#125;
    
    void main&#40;&#41;&#123;
    
    		HANDLE hThread = NULL;
    		HANDLE hThread2 = NULL;
    		DWORD  dwThdId;
    
    		hThread = CreateThread&#40;NULL, 0, ThreadFct, NULL, 0, &dwThdId&#41;;
    
    		hThread2 = OpenThread&#40;THREAD_ACCESS, true, dwThdId&#41;;
    
    		CloseHandle&#40;hThread2&#41;;
    		CloseHandle&#40;hThread&#41;;
    &#125;
    A quoi sert le deuxième paramètre de OpenThread, je comprends rien à cette histoire "d'inhéritance de handle".

  9. #9
    Inactif  
    Avatar de Mac LAK
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    3 893
    Détails du profil
    Informations personnelles :
    Âge : 49
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Octobre 2004
    Messages : 3 893
    Points : 4 846
    Points
    4 846
    Par défaut
    Citation Envoyé par moldavi
    Question: comment fait-on pour créer un thread, le suspendre si besoin, le relancer, tout cela bien sur sans le détruire (sauf à la fin du programme).
    Créer un thread : fonction CreateThread.
    Suspendre un thread : SuspendThread.
    Relancer un thread : ResumeThread.

    Citation Envoyé par moldavi
    Du code s'il vous plaît.
    Heu... La doc de l'API Win32 est suffisamment vaste, non ? Rien que pour chacune de ces fonctions, tu peux avoir des dizaines d'exemples différents suivant le contexte, il n'est pas évident de donner "du code", ça n'est hélas pas aussi trivial que ça... Il faut savoir aussi quel est le contexte d'appel.
    Pour les fonctions aussi simples que SuspendThread et ResumeThread, je te renvoie vers l'aide MSDN : un seul paramètre, le handle sur le thread à suspendre, c'est pas la mort non plus... Pour qu'un thread s'auto-suspende, utilise GetCurrentThread pour obtenir le handle du thread courant.
    Bien sûr, un thread ne peut pas s'auto-relancer, par contre.

    Citation Envoyé par moldavi
    A quoi sert le deuxième paramètre de OpenThread, je comprends rien à cette histoire "d'inhéritance de handle".
    Déjà, fait attention avec la fonction OpenThread : la doc MSDN t'indique clairement, en bas, que cette fonction n'existe pas sur toutes les plate-formes. C'est donc assez risqué de l'utiliser "comme ça".
    De plus, tu n'as pas besoin d'ouvrir un handle sur ton thread : ton appel à CreateThread t'as déjà donné un tel handle ! Pas besoin d'un deuxième ! Il vaut bien mieux "conserver" le handle obtenu à la création, c'est beaucoup plus sûr et portable.

    Pour le 2nd paramètre : un handle, par défaut, n'est pas valide en dehors du thread appelant (et c'est encore pire si c'est lié au processus et non plus aux threads !!). Ce paramètre permet de "donner" le handle à des threads enfants ou à d'autres processus.

    OK ?
    Mac LAK.
    ___________________________________________________
    Ne prenez pas la vie trop au sérieux, de toutes façons, vous n'en sortirez pas vivant.

    Sources et composants Delphi sur mon site, L'antre du Lak.
    Pas de question technique par MP : posez-la dans un nouveau sujet, sur le forum adéquat.

    Rejoignez-nous sur : Serveur de fichiers [NAS] Le Tableau de bord projets Le groupe de travail ICMO

  10. #10
    Inactif  

    Homme Profil pro
    Ingénieur test de performance
    Inscrit en
    Décembre 2003
    Messages
    1 986
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur test de performance
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Décembre 2003
    Messages : 1 986
    Points : 2 605
    Points
    2 605
    Par défaut
    Créer un thread : fonction CreateThread.
    Suspendre un thread : SuspendThread.
    Relancer un thread : ResumeThread.
    faut pas utiliser SuspendThread() / ResumeThread(), c'est à des fins de débogage uniquement. Il faut avoir certains privilèges.
    C'est quoi l'embrouille?

    La MSDN explique bien ce qu'avance Aurelien.Regat-Barrel sauf pour "ResumeThread".

    La doc de l'API Win32 est suffisamment vaste, non ?
    Oui tellement vaste, que c'est impossible de trouver un code aussi simple que le mien, qui fait une chose simple. Je ne demande pas un code qui lance 3000 threads synchronisés, un exemple simple comme le mien.

    Déjà, fait attention avec la fonction OpenThread : la doc MSDN t'indique clairement, en bas, que cette fonction n'existe pas sur toutes les plate-formes
    Pas de souci, je ne développe que pour XP.


    De plus, tu n'as pas besoin d'ouvrir un handle sur ton thread : ton appel à CreateThread t'as déjà donné un tel handle !
    Je récupère le handle de OpenThread pour une utilisation ultérieure avec "WaitForSingleObject" par exemple. Mais peut-être que j'ai mal interprété
    la doc de la MSDN.

    Pour le 2nd paramètre : un handle, par défaut, n'est pas valide en dehors du thread appelant (et c'est encore pire si c'est lié au processus et non plus aux threads !!). Ce paramètre permet de "donner" le handle à des threads enfants ou à d'autres processus
    Oui OK.

  11. #11
    Inactif  

    Homme Profil pro
    Ingénieur test de performance
    Inscrit en
    Décembre 2003
    Messages
    1 986
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur test de performance
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Décembre 2003
    Messages : 1 986
    Points : 2 605
    Points
    2 605
    Par défaut
    Voici un code qui boucle sur le deuxième "WaitForSingleObject":

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    #include <windows.h>
    
    #define THREAD_ACCESS &#40;THREAD_ALL_ACCESS | SYNCHRONIZE | THREAD_TERMINATE | THREAD_QUERY_INFORMATION&#41;
    
    HANDLE g_hThreadEvent = NULL;
    
    DWORD WINAPI ThreadFct&#40;LPVOID&#41;&#123;
    
    		MessageBox&#40;NULL, "thread fonction", "", MB_OK&#41;;
    		if&#40;SetEvent&#40;g_hThreadEvent&#41;&#41;
    		  return 0;
         return -1;
    &#125;
    
    void main&#40;&#41;&#123;
    
    		HANDLE hThread = NULL;
    		DWORD  dwThdId;
    
    		try&#123;
    
             if&#40;!&#40;g_hThreadEvent = CreateEvent&#40;NULL,	FALSE,	FALSE,	NULL&#41;&#41;&#41;
               throw E_POINTER;
    
             if&#40;!&#40;hThread = CreateThread&#40;NULL, 0, ThreadFct, NULL, 0, &dwThdId&#41;&#41;&#41;
    					     	throw E_POINTER;
    
             if&#40;WaitForSingleObject&#40;g_hThreadEvent,INFINITE&#41; == WAIT_FAILED&#41;
    						throw E_FAIL;
    
             if&#40;OpenThread&#40;THREAD_ACCESS, true, dwThdId&#41; == NULL&#41;
    						throw E_FAIL;
    
             if&#40;WaitForSingleObject&#40;g_hThreadEvent,INFINITE&#41; == WAIT_FAILED&#41;
    						throw E_FAIL;
    		&#125;
    		catch&#40;HRESULT&#41;&#123;
    
    	      // MessageBox&#40;...&#41;;
    		&#125;
              if&#40;g_hThreadEvent&#41;
               CloseHandle&#40;g_hThreadEvent&#41;;
    	     if&#40;hThread&#41;
               CloseHandle&#40;hThread&#41;;
    &#125;
    Pourquoi ce code ne fonctionne pas?

  12. #12
    Inactif  
    Avatar de Mac LAK
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    3 893
    Détails du profil
    Informations personnelles :
    Âge : 49
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Octobre 2004
    Messages : 3 893
    Points : 4 846
    Points
    4 846
    Par défaut
    Citation Envoyé par moldavi
    C'est quoi l'embrouille?
    En très résumé : la fonction peut ne pas marcher suivant le type de thread ciblé.
    Ca, c'est la théorie.
    En pratique, sur tes propres threads, ça ne pose jamais de problèmes, surtout si ce sont juste des threads de traitement.
    C'est par contre très risqué si tu "exportes" des threads, ou si tu génères des callbacks au sein de ces threads.

    Citation Envoyé par moldavi
    La MSDN explique bien ce qu'avance Aurelien.Regat-Barrel sauf pour "ResumeThread".
    Disons que si tu ne peux pas suspendre ton thread, ça ne sert à rien de le relancer.

    Citation Envoyé par moldavi
    Oui tellement vaste, que c'est impossible de trouver un code aussi simple que le mien, qui fait une chose simple. Je ne demande pas un code qui lance 3000 threads synchronisés, un exemple simple comme le mien.
    Bon, reprenons depuis le départ : tu veux quoi comme exemple précis ? Que souhaites-tu faire exactement ?

    Citation Envoyé par moldavi
    Pas de souci, je ne développe que pour XP.
    C'est pas pour ça que je te fais remarquer ce point. Ca veut dire aussi que tu es donc censé "verrouiller" ton code de manière à ce qu'il ne s'exécute pas sur d'autres plate-formes.

    Citation Envoyé par moldavi
    Je récupère le handle de OpenThread pour une utilisation ultérieure avec "WaitForSingleObject" par exemple. Mais peut-être que j'ai mal interprété la doc de la MSDN.
    Celui de CreateThread fait la même chose : les handles sur des objets n'ont pas plusieurs "rôles", ils n'ont au mieux qu'un compteur de référence (=nombre de handles ouverts sur un objet donné).
    Mac LAK.
    ___________________________________________________
    Ne prenez pas la vie trop au sérieux, de toutes façons, vous n'en sortirez pas vivant.

    Sources et composants Delphi sur mon site, L'antre du Lak.
    Pas de question technique par MP : posez-la dans un nouveau sujet, sur le forum adéquat.

    Rejoignez-nous sur : Serveur de fichiers [NAS] Le Tableau de bord projets Le groupe de travail ICMO

  13. #13
    Inactif  
    Avatar de Mac LAK
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    3 893
    Détails du profil
    Informations personnelles :
    Âge : 49
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Octobre 2004
    Messages : 3 893
    Points : 4 846
    Points
    4 846
    Par défaut
    Citation Envoyé par moldavi
    Pourquoi ce code ne fonctionne pas?
    Tu veux faire quoi exactement ? Peux-tu me donner le principe de ce que tu veux faire ?

    J'ai présupposé que tu voulais afficher une message box dans ton thread, puis en attendre la fin dans le thread principal... Ce code marche, mais je te conseille de le lancer en ligne de commande à cause des printf montrant le séquencement.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    #include <windows.h>
    #include <stdio.h>
    
    DWORD WINAPI ThreadFct &#40; LPVOID lpParam &#41; &#123;
    
    	HANDLE hEvent = &#40;HANDLE&#41;lpParam ;
    
    	printf&#40;"Starting secondary thread...\n"&#41;;
    	MessageBox&#40;NULL, "Thread function", "", MB_OK&#41;;
    	printf&#40;"Secondary thread &#58; after MessageBox.\n"&#41;;
    	if &#40;SetEvent&#40;hEvent&#41;&#41;
    		return ERROR_SUCCESS ;
    	return ERROR_INVALID_HANDLE ;
    &#125;
    
    void PrintWFSOResult &#40; LPCSTR Prefix, DWORD Result &#41; &#123;
    
    	printf&#40;"%s &#58; ",Prefix&#41;;
    	switch &#40;Result&#41; &#123;
    		case WAIT_FAILED &#58; printf&#40;"Failed.\n"&#41;; break ;
    		case WAIT_ABANDONED	 &#58; printf&#40;"Abandoned.\n"&#41;; break ;
    		case WAIT_TIMEOUT &#58; printf&#40;"Timeout.\n"&#41;; break ;
    		default &#58; printf&#40;"WAIT_OBJECT_%lu.\n",Result&#41;;
    	&#125;
    &#125;
    
    void main &#40; void &#41; &#123;
    
    	HANDLE hThread = NULL;
    	DWORD  dwThdId;
    	HANDLE hEvent ;
    	DWORD WFSOResult ;
    
    	printf&#40;"Starting main thread...\n"&#41;;
    	hEvent = CreateEvent&#40;NULL,FALSE,FALSE,NULL&#41;;
    	hThread = CreateThread&#40;NULL,0,ThreadFct,&#40;LPVOID&#41;hEvent,0,&dwThdId&#41;;
    	printf&#40;"Main thread &#58; after thread creation.\n"&#41;;
    
    	WFSOResult=WaitForSingleObject&#40;hEvent,INFINITE&#41;;
    	PrintWFSOResult&#40;"Main thread &#58; WaitFor Event",WFSOResult&#41;;
    
    	WFSOResult=WaitForSingleObject&#40;hThread,INFINITE&#41;;
    	PrintWFSOResult&#40;"Main thread &#58; WaitFor Thread",WFSOResult&#41;;
    
    	CloseHandle&#40;hEvent&#41;;
    	CloseHandle&#40;hThread&#41;;
    &#125;
    Mac LAK.
    ___________________________________________________
    Ne prenez pas la vie trop au sérieux, de toutes façons, vous n'en sortirez pas vivant.

    Sources et composants Delphi sur mon site, L'antre du Lak.
    Pas de question technique par MP : posez-la dans un nouveau sujet, sur le forum adéquat.

    Rejoignez-nous sur : Serveur de fichiers [NAS] Le Tableau de bord projets Le groupe de travail ICMO

  14. #14
    Inactif  

    Homme Profil pro
    Ingénieur test de performance
    Inscrit en
    Décembre 2003
    Messages
    1 986
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur test de performance
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Décembre 2003
    Messages : 1 986
    Points : 2 605
    Points
    2 605
    Par défaut
    Mac LAK, je te remercie pour ton aide.

    La chose que je veux faire est simple:

    J'aimerais relancer deux fois ThreadFct. La méthode habituelle que j'utilise est la suivante:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    hThread = CreateThread&#40;NULL,0,ThreadFct,&#40;LPVOID&#41;hEvent,0,&dwThdId&#41;;
    CloseHandle&#40;hThread&#41;;
    hThread = CreateThread&#40;NULL,0,ThreadFct,&#40;LPVOID&#41;hEvent,0,&dwThdId&#41;;
    Donc pour lancer deux fois mon thread, je fais deux fois CreateThread.

    J'avais crû comprendre qu'avec OpenThread ou ResumeThread, je pouvais relancer mon thread autant de fois que je le voulais sans réutiliser CreateThread, juste avec le handle.

    Et comme je commence à avoir l'impression que ce n'est pas possible, je me dis vive la MSDN parce que j'ai rien compris. Et c'est pas faute de ne pas l'avoir lu (bon OK je suis nul en anglais).

    L'ambiguité qu'il y a eu, c'est que lorsque je disais relancer un thread, c'était en fait lancer un nouveau thread, un deuxième thread.

    Donc à quoi sert OpenThread, qui d'ailleurs ne fonctionne pas du tout avec les codes que j'ai donné. La fonction OpenThread réussit, mais le thread n'est pas exécuté?????

  15. #15
    Inactif  
    Avatar de Mac LAK
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    3 893
    Détails du profil
    Informations personnelles :
    Âge : 49
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Octobre 2004
    Messages : 3 893
    Points : 4 846
    Points
    4 846
    Par défaut
    Citation Envoyé par moldavi
    Mac LAK, je te remercie pour ton aide.
    De rien.

    Citation Envoyé par moldavi
    J'aimerais relancer deux fois ThreadFct. La méthode habituelle que j'utilise est la suivante <snip>
    C'est la bonne méthode, à un détail près : il te faut 2 variables "HANDLE", et deux DWORD pour les ThreadId. Idéalement, il te faut aussi deux évènements.

    Ce que l'on appelle "lancer" un thread, c'est en fait le créer (CreateThread). La fonction OpenThread ne sert que pour obtenir un handle sur un thread quelconque, en général un thread dont on n'a que le ThreadID et que l'on n'a pas créé soi-même.

    Ensuite, il faut être conscient qu'une fonction thread peut parfaitement être lancée plusieurs fois : c'est pour ça notamment qu'on lui passe des paramètres, parfois c'est simplement un pointeur sur une structure plus ou moins complexe si le thread requiert de nombreux paramètres.
    Pour te donner une idée, j'y passe souvent un pointeur sur une classe C++... ;-)

    Ce code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    HANDLE hThread&#91;2&#93; ;
    DWORD dwThdId&#91;2&#93; ;
    HANDLE hEvent&#91;2&#93; ;
    
    // Il faudra créer deux events au lieu d'un seul, bien sûr.
    hThread&#91;0&#93; = CreateThread&#40;NULL,0,ThreadFct,&#40;LPVOID&#41;hEvent&#91;0&#93;,0,&dwThdId&#91;0&#93;&#41;;
    hThread&#91;1&#93; = CreateThread&#40;NULL,0,ThreadFct,&#40;LPVOID&#41;hEvent&#91;1&#93;,0,&dwThdId&#91;1&#93;&#41;;
    // Reste du code.
    CloseHandle&#40;hThread&#91;0&#93;&#41;;
    CloseHandle&#40;hThread&#91;1&#93;&#41;;
    va lancer deux threads simultanément : est-ce bien ce que tu veux ?

    Citation Envoyé par moldavi
    Donc pour lancer deux fois mon thread, je fais deux fois CreateThread.
    Oui, c'est non négociable : CreateThread est la seule et unique fonction permettant de lancer un thread.

    Citation Envoyé par moldavi
    J'avais crû comprendre qu'avec OpenThread ou ResumeThread, je pouvais relancer mon thread autant de fois que je le voulais sans réutiliser CreateThread, juste avec le handle.
    Ce n'est pas tout à fait vrai :
    - OpenThread te permet effectivement d'obtenir un handle sur le thread quel que soit l'endroit où tu appelles cette fonction. Cependant, il te faut le ThreadID pour obtenir ce handle... Autant conserver celui de CreateThread, et "jeter" le ThreadID si tu n'en as pas l'utilité. Le handle est souvent plus important que le ThreadID.
    - ResumeThread permet bien de reprendre l'exécution d'un thread, mais à condition que celui-ci aie été auparavant suspendu par SuspendThread : ça implique donc que tu as prévu une boucle infinie dans ton thread ("arrêtée" par SuspendThread à chaque étape), plus un système pour terminer le thread proprement.
    Tu vois donc que c'est un peu plus complexe que prévu : de plus, ça ne permet que de contrôler un thread déjà créé par CreateThread, bien sûr.

    Citation Envoyé par moldavi
    Donc à quoi sert OpenThread, qui d'ailleurs ne fonctionne pas du tout avec les codes que j'ai donné. La fonction OpenThread réussit, mais le thread n'est pas exécuté?????
    Cf. ci-dessus ! ;-)

    Est-ce plus clair pour toi ? As-tu besoin d'autres explications ?
    Mac LAK.
    ___________________________________________________
    Ne prenez pas la vie trop au sérieux, de toutes façons, vous n'en sortirez pas vivant.

    Sources et composants Delphi sur mon site, L'antre du Lak.
    Pas de question technique par MP : posez-la dans un nouveau sujet, sur le forum adéquat.

    Rejoignez-nous sur : Serveur de fichiers [NAS] Le Tableau de bord projets Le groupe de travail ICMO

  16. #16
    Inactif  

    Homme Profil pro
    Ingénieur test de performance
    Inscrit en
    Décembre 2003
    Messages
    1 986
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur test de performance
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Décembre 2003
    Messages : 1 986
    Points : 2 605
    Points
    2 605
    Par défaut
    C'est clair qu'une fois l'ambiguité levée, tout est beaucoup plus clair maintenant. (mais il fallait passer par là -> car mauvaise interprétation de la MSDN).

    Après toutes ces choses, mon idée c'est d'utiliser les "Event" afin de relancer le thread sans le recréer. Le truc, je lance le thread une première fois, puis je le met en attente d'un "Event" qui relancera l'exécution, etc... Un autre "Event" me permettra de gérer le thread depuis le programme principal.

    Ca évitera de réutiliser "CreateThread". Je pense que ce sera facile à implémenter. Nickel, merci.

  17. #17
    Inactif  
    Avatar de Mac LAK
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    3 893
    Détails du profil
    Informations personnelles :
    Âge : 49
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Octobre 2004
    Messages : 3 893
    Points : 4 846
    Points
    4 846
    Par défaut
    Citation Envoyé par moldavi
    Après toutes ces choses, mon idée c'est d'utiliser les "Event" afin de relancer le thread sans le recréer. Le truc, je lance le thread une première fois, puis je le met en attente d'un "Event" qui relancera l'exécution, etc... Un autre "Event" me permettra de gérer le thread depuis le programme principal.
    Un peu compliqué au vu de ce que tu veux faire, mais bon, si ça te conviens...

    Citation Envoyé par moldavi
    Ca évitera de réutiliser "CreateThread". Je pense que ce sera facile à implémenter. Nickel, merci.
    Si ton thread fait toujours la même chose, il faut voir aussi sa fréquence d'utilisation et le délai de latence exigé : si c'est respectivement "rarement" et "à l'échelle humaine", autant le recréer avec quelque chose dans ce genre :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    CloseHandle&#40;CreateThread&#40;....&#41;&#41;;
    .
    Comme je l'ai précisé, créer un thread "coûte", en terme de temps CPU. Mais ça reste indiscernable pour un humain ! Un thread inutile et "bloqué" consomme des ressources également (au minimum sa pile, déjà), donc il faut savoir équilibrer la balance suivant tes besoins/contraintes.

    Si par contre le thread est souvent réutilisé et/ou doit avoir une latence faible, la suspension ou l'attente de donnée (event, mutex, section critique, etc...) est une meilleure solution.
    Mac LAK.
    ___________________________________________________
    Ne prenez pas la vie trop au sérieux, de toutes façons, vous n'en sortirez pas vivant.

    Sources et composants Delphi sur mon site, L'antre du Lak.
    Pas de question technique par MP : posez-la dans un nouveau sujet, sur le forum adéquat.

    Rejoignez-nous sur : Serveur de fichiers [NAS] Le Tableau de bord projets Le groupe de travail ICMO

  18. #18
    Inactif  

    Homme Profil pro
    Ingénieur test de performance
    Inscrit en
    Décembre 2003
    Messages
    1 986
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur test de performance
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Décembre 2003
    Messages : 1 986
    Points : 2 605
    Points
    2 605
    Par défaut
    Normalement, je n'ai pas besoin d'utiliser un thread pour ce que je veux faire.

    En fait j'ai un programme principal, avec une priorité normale et une partie multithreadée avec une priorité normale aussi.

    J'utilise les threads à certains moment de mon programme, afin de pouvoir les exécuter avec une priorité basse, et ainsi ne pas perturber et ne pas ralentir la partie multithreadée.

    Actuellement, ce thread issue du programme principale et utilisé avec une priorité basse peut être exécuté dans des ordres de la milliseconde à toutes les 5 minutes.

    Pourquoi ne pas recréer le thread:

    disons que ce thread est utilisé durant toute la durée de vie du programme avec les ordres de fréquence indiqués ci-dessus.

    Même si l'échelle d'utilisation est insignifiante, ce thread devra toujours être présent en mémoire quoiqu'il arrive. Alors bon la pile peut-être rapide, l'attente de l'état signalé (Event) dans le thread peut-être couteux,
    bref, je vais profiler tout ça et voir quel sera le meilleur choix.

  19. #19
    Inactif  
    Avatar de Mac LAK
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    3 893
    Détails du profil
    Informations personnelles :
    Âge : 49
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Octobre 2004
    Messages : 3 893
    Points : 4 846
    Points
    4 846
    Par défaut
    Citation Envoyé par moldavi
    Même si l'échelle d'utilisation est insignifiante, ce thread devra toujours être présent en mémoire quoiqu'il arrive. Alors bon la pile peut-être rapide, l'attente de l'état signalé (Event) dans le thread peut-être couteux,
    bref, je vais profiler tout ça et voir quel sera le meilleur choix.
    Un ResumeThread induit une latence plus grande qu'un WaitForSingleObject, mais pour garantir une reprise "immédiate" de ton code, je te propose ce genre d'astuce :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    // Passage en priorité haute.
    SetThreadPriority&#40;GetCurrentThread&#40;&#41;,THREAD_PRIORITY_HIGHEST&#41;;
    // Attente de l'évènement &#58; ceci va bloquer le thread.
    WaitForSingleObject&#40;....&#41;;
    // On repasse en priorité normale, ou basse.
    SetThreadPriority&#40;GetCurrentThread&#40;&#41;,THREAD_PRIORITY_BELOW_NORMAL&#41;;
    Ce code permet de reprendre le thread le plus rapidement possible, c'est un truc qui marche pas mal du tout en général.

    camboui : T'es passé où, mon gars ??? ;-)
    Mac LAK.
    ___________________________________________________
    Ne prenez pas la vie trop au sérieux, de toutes façons, vous n'en sortirez pas vivant.

    Sources et composants Delphi sur mon site, L'antre du Lak.
    Pas de question technique par MP : posez-la dans un nouveau sujet, sur le forum adéquat.

    Rejoignez-nous sur : Serveur de fichiers [NAS] Le Tableau de bord projets Le groupe de travail ICMO

  20. #20
    Inactif  

    Homme Profil pro
    Ingénieur test de performance
    Inscrit en
    Décembre 2003
    Messages
    1 986
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur test de performance
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Décembre 2003
    Messages : 1 986
    Points : 2 605
    Points
    2 605
    Par défaut
    Ce code permet de reprendre le thread le plus rapidement possible, c'est un truc qui marche pas mal du tout en général.
    C'est le genre d'astuce trop utile et intéressant.

    camboui : T'es passé où, mon gars ???
    Je voudrais lui signaler que j'ai squatté son message, parce que je pense que ma question rejoignait un peu la sienne, et que les réponses pouvaient l'intéresser.

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 3 123 DernièreDernière

Discussions similaires

  1. Création et destruction dynamique de composants visuels
    Par Dr.Gang dans le forum C++Builder
    Réponses: 3
    Dernier message: 16/10/2009, 17h19
  2. Destruction de thread
    Par oli_carbo dans le forum Général Java
    Réponses: 3
    Dernier message: 12/06/2009, 15h03
  3. [VB.NET] Création MDIChild dans un thread différent
    Par XnoTonio dans le forum Windows Forms
    Réponses: 5
    Dernier message: 19/05/2006, 15h53
  4. Problème création et destruction de processus.
    Par loupdeau dans le forum MFC
    Réponses: 5
    Dernier message: 08/04/2005, 13h33

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo