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

MFC Discussion :

Comment arrêter un thread de travail ? (version FAQ)


Sujet :

MFC

  1. #1
    Membre régulier Avatar de loupdeau
    Profil pro
    Inscrit en
    Janvier 2005
    Messages
    125
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Janvier 2005
    Messages : 125
    Points : 79
    Points
    79
    Par défaut Comment arrêter un thread de travail ? (version FAQ)
    Salut,

    Je sais pas si c'est parce qu'il est tard, mais j'ai un peu de mal avec la solution présenté par la FAQ pour arrêter un thread.
    Est-ce que ca permet bien de pouvoir stopper un thread en cours quand on veut?

    Si oui, un petit exemple siouplé.

    Merci

  2. #2
    Membre éprouvé Avatar de Caine
    Inscrit en
    Mai 2004
    Messages
    1 028
    Détails du profil
    Informations personnelles :
    Âge : 52

    Informations forums :
    Inscription : Mai 2004
    Messages : 1 028
    Points : 1 122
    Points
    1 122
    Par défaut
    Un petit lien vers la FAQ n'aurait pas fait de mal.

    Donc ici :http://c.developpez.com/faq/vc/?page...opWorkerThread

    Le principe est simple:
    Dans la fonction du thread, on sort de la boucle si un evènement spécifique est signalé.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
      while(true)
      {
        // attente evenement de fin du thread. -> l’objet doit être ‘signalé ‘
       // WaitForSingleObject  renvoie WAIT_OBJECT_0 si l’objet est signalé.
        if(::WaitForSingleObject(pThis->m_EndThread, 0) == WAIT_OBJECT_0)
     
        {
          // signale l'objet event d'attente et sort.
          ::SetEvent(pThis->m_WaitThread);
          return 0;
        }
    Losrque l'application veut tuer le thread, elle force l'évènement de terminaison du thread en appellant explicitemant le destructeur de l'objet CWorkerThread.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
      // declenche la fin du thread
      ::SetEvent(m_EndThread);
     
      // attend que le thread soit terminé
      ::WaitForSingleObject(m_WaitThread, INFINITE);
    Enfin, ce n'est pas la seule méthoe, tu peux aussi te pencher du côté de TerminateThread.

  3. #3
    Rédacteur
    Avatar de farscape
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Novembre 2003
    Messages
    9 055
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2003
    Messages : 9 055
    Points : 17 323
    Points
    17 323
    Par défaut
    re,
    Bien il me semble que le post était clair non ?
    Attention a terminatethread voir l’avertissement sur la libération mémoire .

  4. #4
    Membre régulier Avatar de loupdeau
    Profil pro
    Inscrit en
    Janvier 2005
    Messages
    125
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Janvier 2005
    Messages : 125
    Points : 79
    Points
    79
    Par défaut
    J'avoue que concernant l'utilisation j'avais beaucoup de doutes...

    J'ai un problème à la compilation concernant la ligne :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    AfxBeginThread(ThreadFunction, this);
    J'obtiens toujours l'erreur :
    'AfxBeginThread' : none of the 2 overloads can convert parameter 1 from type 'unsigned int (void ** )'

  5. #5
    Membre éprouvé Avatar de Caine
    Inscrit en
    Mai 2004
    Messages
    1 028
    Détails du profil
    Informations personnelles :
    Âge : 52

    Informations forums :
    Inscription : Mai 2004
    Messages : 1 028
    Points : 1 122
    Points
    1 122
    Par défaut
    Tu as simplement créer ton projet SANS les MFC !

    Dans ce cas, il te reste deux choix:
    - recréer le projet avec les MFC.
    - Oublier la classe CThread et repartir de l'API Windows pûre.

    Pour Farscape: Oui, il faut effectivement faire attention aux problèmes de libération de mémoire. Dans le même temps, j'ai indiqué ça surtout s'il n'utilisait pas le MFC, j'ai manqué de clarté.

  6. #6
    Membre régulier Avatar de loupdeau
    Profil pro
    Inscrit en
    Janvier 2005
    Messages
    125
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Janvier 2005
    Messages : 125
    Points : 79
    Points
    79
    Par défaut
    En remplacant
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    ThreadFunction(LPVOID* pvParam)
    par
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    ThreadFunction(LPVOID pParam)
    Ca fonctionne...

    Pour pouvoir passer plusieurs parametre à mon Thread j'ai essayé ca :

    Classe:
    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
    class CWorkerThread
    {
    ...
    private:
    ...
      struct Parameters{
    	CWorkerThread* pWorkerThread;
    	CProject* pProject;
    	};
      Parameters arg;
    ...
    };
     
    CWorkerThread::CWorkerThread()
    {
      ...
      arg.pWorkerThread=this;
      arg.pProject=NULL;
     
     AfxBeginThread(ThreadFunction, &arg);
    }
    ThreadFunction :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    UINT CWorkerThread::ThreadFunction(LPVOID pParam)
    {
      CWorkerThread *pThis = static_cast<Parameters*>(pParam)->pWorkerThread;
      CProject *pProject = static_cast<Parameters*>(pParam)->pProject;
     
      while(true)
      {
       ....
        // Votre code de traitement.
        if(pProject) pProject->start();
      }
      return 0;
    }
    Mais ca ne fait que planter...
    memory could not be "read"
    Quand j'essaye d'accéder à pProject ou pThis...

  7. #7
    Rédacteur
    Avatar de farscape
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Novembre 2003
    Messages
    9 055
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2003
    Messages : 9 055
    Points : 17 323
    Points
    17 323
    Par défaut
    salut
    vu ton code ça me semble normal.
    dans ton constructeur tu as arg.pProject=NULL;
    et apres tu tentes d'utiliser le pointeur dans ton thread ,mais la valeur est toujours a null ,d'ou le plantage.

  8. #8
    Membre régulier Avatar de loupdeau
    Profil pro
    Inscrit en
    Janvier 2005
    Messages
    125
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Janvier 2005
    Messages : 125
    Points : 79
    Points
    79
    Par défaut
    Pourtant j'avais bien penser à tester si le pointeur n'est pas nul :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    if(pProject) pProject->start();
    Je viens de tester autrement, en déclarant arg comment un pointeur :
    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
    class CWorkerThread 
    { 
    ... 
    private: 
    ... 
      struct Parameters{ 
       CWorkerThread* pWorkerThread; 
       CProject* pProject; 
       }; 
      [b]Parameters* arg; [/b]
    ... 
    }; 
     
    CWorkerThread::CWorkerThread() 
    { 
      ... 
      arg = new Parameters;
      arg->pWorkerThread=this; 
      arg->pProject=NULL; 
     
     AfxBeginThread(ThreadFunction, arg); 
    }
    Et cette fois ca fonctionne !.

    : Est-ce que précédemment : static_cast<Parameters*>(pParam) était valable alors que j'avais passé en paramètre &arg ?

    : Est-ce que la solution de la FAQ (UINT ThreadFunction(LPVOID* pvParam)) ne marche vraiment pas, ou est-ce que j'ai mal fait quelque chose ?

    Cain :
    Tu as simplement créer ton projet SANS les MFC !
    Là t'as visiblement faux...

  9. #9
    Expert éminent sénior

    Homme Profil pro
    pdg
    Inscrit en
    Juin 2003
    Messages
    5 753
    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 753
    Points : 10 704
    Points
    10 704
    Billets dans le blog
    3
    Par défaut
    Losrque l'application veut tuer le thread, elle force l'évènement de terminaison du thread en appellant explicitemant le destructeur de l'objet CWorkerThread

    faut pas appeler explicitement un destructeur, sauf cas très particulier. http://c.developpez.com/faq/cpp/?pag...teur_explicite

  10. #10
    Membre régulier Avatar de loupdeau
    Profil pro
    Inscrit en
    Janvier 2005
    Messages
    125
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Janvier 2005
    Messages : 125
    Points : 79
    Points
    79
    Par défaut
    Dans ma classe CProcessDlg (CDialog) j'ai un membre privé : CWorkerThread* m_threadStart;

    Dans BOOL CProcessDlg::OnInitDialog() je crée mon CWorkThread en passant en parametre mon Projet :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    m_threadStart = new CWorkerThread(pMyProjectDoc->getProject());
    Tout va bien,
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    if(pProject) pProject->start();
    s'exécute bien dans le thread.

    Quand je clique sur le bouton "Annuler" de mon CDialog, je veux arrêter le thread.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    void CProcessDlg::OnCancel() 
    {
      delete m_threadStart;
     
      CDialog::OnCancel();
    }
    "delete m_threadStart;" doit normalement appelé le destructeur de CWorkerThread, et donc arrêter le thread comme prévu...

    Ca ne fonctionne pas, mon thread tourne en boucle... et je suis obligé de le killer...


  11. #11
    Rédacteur
    Avatar de farscape
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Novembre 2003
    Messages
    9 055
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2003
    Messages : 9 055
    Points : 17 323
    Points
    17 323
    Par défaut
    re,
    En fait le post est erroné dans la faq il y eu un mélange de code la fonction du thread doit être bien statique a la classe.
    la version corrigée serait:
    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
     
    class CMyDialog : public CDialog
    {
    public:
        CMyDialog(CWnd* pParent = NULL)
            : CDialog(100, pParent)
        {
            m_pThread = NULL;
        }
     
        bool InitThread()
        {
            m_pThread = AfxBeginThread(ThreadFunc, this);
            if(!m_pThread)
            {
                // Impossible de créer le thread !
                return false;
            }
            return true;              
        }
     
    private:
        CWinThread *m_pThread;            
     
        static UINT ThreadFunc(LPVOID pvParam);
    };
    UINT CMyDialog::ThreadFunc(LPVOID pvParam)
    {
        CMyDialog  *pThis=static_cast< CMyDialog *>( pvParam) ;
        // Votre code
        //
        return 0 ;
    }

  12. #12
    Membre régulier Avatar de loupdeau
    Profil pro
    Inscrit en
    Janvier 2005
    Messages
    125
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Janvier 2005
    Messages : 125
    Points : 79
    Points
    79
    Par défaut
    En fait il faut complètement "oublier" la classe CWorkerThread du post de la FAQ et intégrer directement toutes ses fonctionnalités à CMyDialog.
    C'est bien ca ?

  13. #13
    Membre éprouvé Avatar de Caine
    Inscrit en
    Mai 2004
    Messages
    1 028
    Détails du profil
    Informations personnelles :
    Âge : 52

    Informations forums :
    Inscription : Mai 2004
    Messages : 1 028
    Points : 1 122
    Points
    1 122
    Par défaut
    Citation Envoyé par loupdeau
    En remplacant
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    UINT ThreadFunction(LPVOID* pvParam)
    par
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    UINT CWorkerThread::ThreadFunction(LPVOID pParam)
    Ca fonctionne...
    Autant pour moi, j'avais pas vu

    Normal, une méthode d'objet est toujours utilisé comme suit : NomObjet::Methode_objet(liste arguments).

    Et justement StdAfxBeginThread attend un pointeur vers la fonction de traitement.

  14. #14
    Expert éminent sénior

    Homme Profil pro
    pdg
    Inscrit en
    Juin 2003
    Messages
    5 753
    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 753
    Points : 10 704
    Points
    10 704
    Billets dans le blog
    3
    Par défaut
    C'est bon aussi dans la thread, c'est juste que le prototype de la FAQ est erroné : LPVOID* au lieu de LPVOID.

  15. #15
    Membre éprouvé Avatar de Caine
    Inscrit en
    Mai 2004
    Messages
    1 028
    Détails du profil
    Informations personnelles :
    Âge : 52

    Informations forums :
    Inscription : Mai 2004
    Messages : 1 028
    Points : 1 122
    Points
    1 122
    Par défaut
    Comme ça m'est arrivé avec un thread de traitement, je pense utile de pévenir notre ami de ce problème:

    si le thread est réellement bloqué suite à une fontcion blouante, il ne revien pas au debut de sa boucle, ou à l'instruction de test, et ainsi, il ne se terminera pas même si l'évènement est envoyé.

    Terminer proprement un thread devrait suivre cet alogorihtme:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    appeller la méthode(procèdure) de terminison de thread de la classe(librairie).
    Attendre l'expiration d'un timeout.
    Si le thread n'est toujours pas terminé alors
       le tuer brutalement via TerminateThread (ou plus violent)
    Bien sûr, dans ce cas toute ressources allouées dynamiquement par le thread dans le cas d'une terminaison violente resteront allouées.

    C'est pour cette raison que j'évite au maximum d'allouer des ressources lourdes dans un thread, je préfère que l'appellant les alloue et passes un structure de pointeurs au thread via pArg.

  16. #16
    Membre régulier Avatar de loupdeau
    Profil pro
    Inscrit en
    Janvier 2005
    Messages
    125
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Janvier 2005
    Messages : 125
    Points : 79
    Points
    79
    Par défaut
    Merci pour ses précisions.

    Je viens d'adapter ma classe CMyDialog en fonction de la classe CWorkerThread.

    CMyDialog possèd donc 2 handler privés : m_EndThread et m_WaitThread

    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
    48
    49
    50
    51
    52
    53
    BOOL CProcessDlg::OnInitDialog() 
    {
      ...	
      InitThread();
      ...
    }
     
    bool CProcessDlg::InitThread()
    {
    	HANDLE m_EndThread = CreateEvent(0, TRUE, FALSE, 0);
    	HANDLE m_WaitThread = CreateEvent(0, TRUE, FALSE, 0);
     
    	m_pThread = AfxBeginThread(ThreadFunc, this); 
    	if(!m_pThread) 
    	{ 
    		// Impossible de créer le thread ! 
    		return false; 
    	} 
    	return true; 
    }
     
    UINT CProcessDlg::ThreadFunc(LPVOID pvParam)
    {
     
    CProcessDlg  *pThis=static_cast<CProcessDlg*>(pvParam);
    while(true)
     {
       if(WaitForSingleObject(pThis->m_EndThread, 0) == WAIT_OBJECT_0)
       {
        SetEvent(pThis->m_WaitThread);
        return 0;
       }
     
      pThis->m_pProject->start();
     
     }
    return 0;
    }
     
    void CProcessDlg::OnCancel() 
    {
    	// declenche la fin du thread
    	SetEvent(m_EndThread);
     
    	// attend que le thread soit terminé
    	WaitForSingleObject(m_WaitThread, INFINITE);
     
    	// fermeture dans handles
    	CloseHandle(m_EndThread);
    	CloseHandle(m_WaitThread);
     
    	//CDialog::OnCancel();
    }
    Le fait de cliquer sur "Cancel" doit provoquer l'arrêt du Thread. Mais ca ne marche pas, il tourne en boucle...

    Si quelqu'un pouvait m'expliquer...

  17. #17
    Rédacteur
    Avatar de farscape
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Novembre 2003
    Messages
    9 055
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2003
    Messages : 9 055
    Points : 17 323
    Points
    17 323
    Par défaut
    re,
    je comprends pas la:
    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
     
    bool CProcessDlg::[b]InitThread()[/b]
    {
       //HANDLE m_EndThread = CreateEvent(0, TRUE, FALSE, 0);
    //   HANDLE m_WaitThread = CreateEvent(0, TRUE, FALSE, 0);
     
        m_EndThread = CreateEvent(0, TRUE, FALSE, 0);
        m_WaitThread = CreateEvent(0, TRUE, FALSE, 0);
     
       m_pThread = AfxBeginThread(ThreadFunc, this);
       if(!m_pThread)
       {
          // Impossible de créer le thread !
          return false;
       }
       return true;
    }
    les variables m_EndThread ne doivent pas etre locales a la fonction initthread mais locale a la classe....

  18. #18
    Membre régulier Avatar de loupdeau
    Profil pro
    Inscrit en
    Janvier 2005
    Messages
    125
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Janvier 2005
    Messages : 125
    Points : 79
    Points
    79
    Par défaut
    J'suis trop bete. J'avais effectivement m_EndThread et m_WaitThread en variable privé de ma classe.
    Mais par un copier-coller malheureux, dans InitThread j'en ai redéfinie deux nouvelles au lieu d'affecter celles existantes.

    Et maintenant, ca marche !!! Merci !!!


    Dans mon thread j'exécute une suite de fichier exe avec ShellExecute grace à la fonction start(). Quand je clique sur annuler, il faut attendre que tout les exe aient été exécuté pour que ca se termine...
    Y a t'il un moyen simple pour interrompre la fonction "start();" dès que l'utilisateur clique sur annuler ?

  19. #19
    Expert éminent sénior

    Homme Profil pro
    pdg
    Inscrit en
    Juin 2003
    Messages
    5 753
    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 753
    Points : 10 704
    Points
    10 704
    Billets dans le blog
    3
    Par défaut
    C'est la modification que j'ai proposé dans la FAQ:
    http://www.developpez.net/forums/vie...800489#1800489

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Comment arrêter un Thread proprement
    Par takinelinfo dans le forum C#
    Réponses: 9
    Dernier message: 08/01/2018, 08h42
  2. Comment arrêter mon Thread ?
    Par avenger22 dans le forum Général Java
    Réponses: 10
    Dernier message: 06/04/2014, 10h27
  3. Comment arréter un thread qui exécute une instruction bloquante
    Par nibor2luxe dans le forum Concurrence et multi-thread
    Réponses: 15
    Dernier message: 28/02/2008, 17h03
  4. Réponses: 14
    Dernier message: 04/06/2007, 22h43
  5. [java.util.Timer]Comment arrêter l'exécution d'un Thread
    Par Invité dans le forum Concurrence et multi-thread
    Réponses: 1
    Dernier message: 07/06/2006, 07h54

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