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 :

Conseils sur le thread


Sujet :

MFC

  1. #1
    Membre émérite Avatar de neptune
    Profil pro
    Inscrit en
    Novembre 2003
    Messages
    835
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Novembre 2003
    Messages : 835
    Par défaut Conseils sur le thread
    Bonjour à tous, je dois écrire du code avec des threads et ma faible expérience dans ce domaine me pousse à venir demander conseil avant.

    Voici ce que je dois faire...

    Lorsque j'instancie mon objet, créez un thread.
    Dans ce thread, vérifier quelquechose à interval régulier.
    A la destruction de l'objet, arretez le thread.
    Dans l'arret du thread, faire une série de choses (style fermeture de fichier).

    Et voici comment je vois les choses, j'espère ne pas faire de grosses erreurs et j'espere avoir l'avis de programeur rodés à ce genre de manipulations.

    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
    54
    55
    56
    57
    58
     
    // maclasse.h
    #pragma once
    #include "maclasseabstraite.h"
     
    class CMaClasse :
    	public CMaClasseAbstraite
    {
    public:
    	CMaClasse();
    	~CMaClasse(VOID);
     
    private:
    	HANDLE _thread;
    	BOOL _continue;
     
    	static DWORD WINAPI MaFonctionThread(LPVOID p);
    };
     
    // maclasse.cpp
    #include "StdAfx.h"
    #include "maclasse.h"
     
    CRITICAL_SECTION CriticalSection;
     
    CMaClasse::CMaClasse()
    {
    	_continue = TRUE;
    	InitializeCriticalSectionAndSpinCount(&CriticalSection, 0x80000400);
    	_thread = CreateThread(NULL, 0, MaFonctionThread, this, 0, NULL);
    }
     
    CMaClasse::~CMaClasse(VOID)
    {
    	EnterCriticalSection(&CriticalSection);
    	_continue = FALSE;
    	LeaveCriticalSection(&CriticalSection);
     
    	WaitForSingleObject(_thread, 2000);
    	CloseHandle(_thread);
     
    	DeleteCriticalSection(&CriticalSection);
    }
     
    DWORD WINAPI CMaClasse::MaFonctionThread(LPVOID p)
    {
    	CMaClasse *pThis = reinterpret_cast<CMaClasse*>(p);
     
    	EnterCriticalSection(&CriticalSection);
    	while (pThis->_continue)
    	{
    		// Do your job
    		Sleep(1000);
    	}
    	LeaveCriticalSection(&CriticalSection);
     
    	return(0);
    }
    Merci de votre lecture!

  2. #2
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 393
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 393
    Par défaut
    C'est pas mal, mais:
    1) Décare ton booléen en volatile, ça vaut mieux
    2) Déplace ta section critique dans le thread, car là, il la garde en continu ---> ça débouchera sur une "étreinte fatale".

    D'ailleurs, sous Win32, l'affectation d'une variable 32bits est atomique: Tu n'est pas obligé d'utiliser un objet de synchronisation.
    Mais si tu tiens à en utiliser un, utilise un Event à la place du booléen: CreateEvent() dans le constructeur, SetEvent() pour quitter le thread, qui regarde avec WaitForMultipleObject(hEvent, 0); entre chaque Sleep()
    (Sans oublier le CloseHandle() qui va avec dans le destructeur).
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  3. #3
    Membre émérite Avatar de neptune
    Profil pro
    Inscrit en
    Novembre 2003
    Messages
    835
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Novembre 2003
    Messages : 835
    Par défaut
    Merci pour ces conseils. Pour le point 1, une explication supplémentaire me ferait du bien. Enfin, le "ça vaut mieux" m'interpèle... :-)

    Sinon, j'ai modifié le code de cette façon. Ai-je bien compris où tu voulais en venir?

    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
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
     
    // maclasse.h 
    #pragma once 
    #include "maclasseabstraite.h" 
     
    class CMaClasse : 
       public CMaClasseAbstraite 
    { 
    public: 
       CMaClasse(); 
       ~CMaClasse(VOID); 
     
    private: 
       HANDLE _thread; 
       //BOOL _continue;
       HANDLE _threadMustQuit;
     
       static DWORD WINAPI MaFonctionThread(LPVOID p); 
    }; 
     
    // maclasse.cpp 
    #include "StdAfx.h" 
    #include "maclasse.h" 
     
    CRITICAL_SECTION CriticalSection; 
     
    CMaClasse::CMaClasse() 
    { 
       //_continue = TRUE;
       _threadMustQuit = CreateEvent(NULL, FALSE, FALSE, NULL);
       InitializeCriticalSectionAndSpinCount(&CriticalSection, 0x80000400); 
       _thread = CreateThread(NULL, 0, MaFonctionThread, this, 0, NULL); 
    } 
     
    CMaClasse::~CMaClasse(VOID) 
    { 
    	 /*
       EnterCriticalSection(&CriticalSection); 
       _continue = FALSE; 
       LeaveCriticalSection(&CriticalSection); 
       */
       SetEvent(_threadMustQuit);
     
       WaitForSingleObject(_thread, 2000); 
       CloseHandle(_thread); 
       CloseHandle(_threadMustQuit);
     
       DeleteCriticalSection(&CriticalSection); 
    } 
     
    DWORD WINAPI CMaClasse::MaFonctionThread(LPVOID p) 
    { 
       CMaClasse *pThis = reinterpret_cast<CMaClasse*>(p); 
       BOOL mustContinue = TRUE;
     
       //EnterCriticalSection(&CriticalSection); 
       //while (pThis->_continue) 
       while (mustContinue) 
       { 
          Sleep(1000);
     
          EnterCriticalSection(&CriticalSection);
          // Do your job
          LeaveCriticalSection(&CriticalSection); 
     
          if (WaitForMultipleObject(_threadMustQuit, 0) == WAIT_OBJECT_0) mustContinue = FALSE;
       } 
       //LeaveCriticalSection(&CriticalSection); 
     
       return(0); 
    }

  4. #4
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 393
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 393
    Par défaut
    Hé bien, ton code m'a l'air parfait, sauf qu'ici, la CRITICAL_SECTION interne ne sert pas à grand chose, puisque seuls la classe et le thread y auront accès.

    Dans ce cas, soit tu la vires complètement, soit tu la rends utile en développant un accesseur qui retourne son adresse (ou mieux, directement des méthodes enter(), tryEnter() et leave())

    Pour le coup du volatile, je pense que c'est la doc de microsoft qui parle le mieux:
    http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vccelng/htm/key_s-z_10.asp
    The volatile keyword is a type qualifier used to declare that an object can be modified in the program by something other than statements, such as the operating system, the hardware, or a concurrently executing thread.
    Bref, ça indique au compilo qu'un autre thread peut le modifier à tout moment.
    Sinon, l'optimiseur peut te virer un test dans une boucle, en le considérant toujours faux.
    Par exemple, imagine cette boucle, avec bQuit modifié par un autre thread pour dire à ce thread-ci de quitter:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    while( bQuit == FALSE )
    	{
    	Sleep(1000);
    	}
    Si bQuit n'est pas déclaré volatile, le compilo risque de transformer ta Boucle en l'équivalent de ceci:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    if(bQuit == FALSE)
    	{
    	while( TRUE )
    		{
    		Sleep(1000);
    		}
    	}
    Ce qui sera une boucle infinie...
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  5. #5
    Membre éclairé
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Juin 2005
    Messages
    700
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : Tourisme - Loisirs

    Informations forums :
    Inscription : Juin 2005
    Messages : 700
    Par défaut
    J'ai tres recement fais une chose d'assez similaire et comme toi j'ai plongé dans les méandres du multithreading. Médinoc m'a d'ailleur bien aidé (mais ne m'avait pas parlé des volatiles le vilain ! ).

    Vu ce que tu veux faire, je dois te mettre en garde d'une chose. Plus tu aura d'instances de thread, plus ca va etre chaud à gerer, le multithread a debugger, c'est tout simplement l'horreur.

    Alors question : as tu vraiment besoin que chaque object ai un thread qui s'execute constamment? (tout depend du nombre d'objet instanciable).

    Plus tu en aura plus tes threads seront "fragmentés" en un sens, et donc, plus le resultat tardera à venir et/ou viendra un peu quand il veut...

    La technique que j'ai employé de mon coté, ca a été de mettre en place un compteur de reference, un thread static à la classe, et un timer.

    Lorsque le premier objet est instancié, on lance le thread qui ressemble au tient et on lance un timer qui toutes les n milliseconde reveille ce thread. Et ce thread en fonction du compteur de référence va faire sa vérif sur tous les objets instanciés.

    l'avantage de ce systeme, c'est que tu n'a plus que 3 threads : celui de l'appli, celui du timer, et celui du thread de vérif. Et sur ce thread de vérif, tu lui met la priorité que tu veux.

    voici en gros ce que ca donne (je te passe la partie constructeur, et compteur de ref):

    header :
    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
    class CXC64IOmodule
    {
    	private:
    //Les statiques
    	static unsigned short s_usObjectCount;			//nbr d'objets instanciés
     
    	static HANDLE s_htTrigGlobalgrab;				//Handle du thread qui check tous les Inputs
    	static HANDLE s_hTimer;							//Timer pour la fonction InputsListener()
    	static HANDLE s_hTimerQueue;					//Timerqueue pour la fonction InputsListener()
     
    static DWORD WINAPI InputsListener(LPVOID lpParameter);
    static DWORD WINAPI TrigGrabber(LPVOID lpParameter);
     
    	static BOOL s_bRunThreadInputsListener;			//Flag controlant la sortie du thread nommé.
    	static HANDLE s_heventTopTimer;					//Evenement envoyé par le timer toutes les n Millisecondes.
     
     
    	HANDLE LaunchInputsListener(DWORD period, UINT priority= THREAD_PRIORITY_HIGHEST);  //Lance une routine qui va cycliquement metre a jour l'etat des input (tableau Input) attributs : Periode du timer en millisecondes, priorité du thread.
    	void StopInputsListener();
    };
     
    //Threads
     
     
    void CALLBACK TimerRoutine(PVOID lpParam, BOOLEAN TimerOrWaitFired);

    le fichier source:


    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
    void CXC64IOmodule::StopInputsListener()
    {
    	//On fait ce que tu as mis dans ton code pour arretter les threads
    }
     
    HANDLE CXC64IOmodule::LaunchInputsListener(DWORD period, UINT priority)
    {
    	s_bRunThreadInputsListener=TRUE;
    	if(!s_heventTopTimer)s_heventTopTimer=CreateEvent(NULL,FALSE,FALSE,NULL);
    	ResetEvent(s_heventTopTimer); //Le thread va donc immediatement se mettre en someil et sera reveillé par le timer
    	if(!s_htTrigGlobalgrab)s_htTrigGlobalgrab=CreateThread(NULL,0,&InputsListener,&priority,0,NULL); //On le lance
    	//On crée maintenant le timer
    	s_hTimerQueue = CreateTimerQueue();
    	if (!s_hTimerQueue) AfxMessageBox("Probleme de Creation du timer");
    	CreateTimerQueueTimer(&s_hTimer, s_hTimerQueue, TimerRoutine, NULL , 0, period, WT_EXECUTEINTIMERTHREAD);
     
    	return s_htTrigGlobalgrab;
    }
     
    //////////////////////////////////////TIMER///////////////////////////////////////////
     
    void CALLBACK TimerRoutine(PVOID lpParam, BOOLEAN TimerOrWaitFired)
    {
    	SetEvent(CXC64IOmodule::s_heventTopTimer);
    	return;
    }
     
    //////////////////////////////////////THREADS/////////////////////////////////////////
     
    DWORD WINAPI CXC64IOmodule::InputsListener(LPVOID lpParameter)
    {
    	unsigned int i=0;
    	UINT* priority = (UINT*)  lpParameter;
     
    	SetThreadPriority(GetCurrentThread(),*priority);
    	//Tant qu'on ne demande pas au thread de sortir
    	while(s_bRunThreadInputsListener)
    	{
    		//On attends le top du timer
    		WaitForSingleObject(s_heventTopTimer,INFINITE);
     
    		//Pour toutes les pins d'input
    		for(i=0 ; i< s_usObjectCount ; i++)
    		{
    			//Fait ce qu'il a a faire sur chaque objet			
    		}
    	}
    	return 0L;
    }
    Dans ton cas, LaunchInputListener serait appellé par le constructeur si s_usObjectCount ==1. Et StopInputsListener par le destructeur quand s_usObjectCount atteinds 0.

    voila comme tu vois dans mon code je n'utilise pas de volatile, ca sera corrigé aussitot revenu au boulot

    Pour info avec cette technique, j'arrive à syncroniser des evenement avec 5 ms de tolérence !!! mon thread est en priorité RealTime, et je n'ai aucun effet de bord du style pc figé puisque ce thread dors la pluspart du temps et s'execute en moins de 10 millisecondes.

  6. #6
    Membre émérite Avatar de neptune
    Profil pro
    Inscrit en
    Novembre 2003
    Messages
    835
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Novembre 2003
    Messages : 835
    Par défaut
    Merci pour vos réponses!

    Pour répondre rapidement aux questions: je n'aurai qu'une seule instance de cet objet, donc pas trop de souci pour le thread.

    Pour ce qui est de "volatile" c'est exactement ce que j'avais lu à son sujet, mais la doc disant pouvant etre modifié par l'operating system, je n'ai pas jugé bon de l'utiliser. Quoi qu'il en soit, je travail maintenant avec un évènement donc ceci ne s'applique plus à mon problème.

    Par contre, ma critical section est encore nécessaire, en effet, je ne l'avais pas mis dans le code source pour ne pas surcharger le post, mais j'accède a des variables au sein de mon objet dans les 2 threads en meme temps.

    Je vais devoir implémenter un second thread, celui-ci ne se créant pas systèmatiquement au démarrage. Et là, une nouvelle question apparait...

    Dans le thread principal, je crée le nouveau thread pour qu'il fasse son boulot. Comment puis-je savoir, sans bloquer le thread principal, que le thread de travail est terminé (afin de faire un CloseHandle)? Dois je avoir un timer qui le vérifie régulièrement?

Discussions similaires

  1. Conseil sur l'application et gestion d'un thread
    Par Karl Marx dans le forum Threads & Processus
    Réponses: 4
    Dernier message: 24/02/2014, 16h30
  2. Conseil sur les thread dans une dll
    Par ksoft dans le forum C
    Réponses: 2
    Dernier message: 30/03/2009, 15h12
  3. Besoin d'explications et de conseils sur les threads
    Par matteli dans le forum Développement 2D, 3D et Jeux
    Réponses: 12
    Dernier message: 02/03/2008, 17h27
  4. Recherche Livre / Conseils sur la conception de Base
    Par Yeuma dans le forum Décisions SGBD
    Réponses: 7
    Dernier message: 02/01/2004, 14h25
  5. Question simple sur les threads :)
    Par momox dans le forum C++Builder
    Réponses: 2
    Dernier message: 15/06/2003, 04h13

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