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

wxWidgets Discussion :

Thread et Sleep


Sujet :

wxWidgets

  1. #1
    agh
    agh est déconnecté
    Membre du Club
    Inscrit en
    Juillet 2002
    Messages
    51
    Détails du profil
    Informations forums :
    Inscription : Juillet 2002
    Messages : 51
    Points : 49
    Points
    49
    Par défaut Thread et Sleep
    Bonjour à tous,

    je débute en wxWidgets, qui me semble pas mal du tout.
    Je vous expose la situation :

    j'ai une classe qui s'appelle Com et qui s'occupe de communiquer avec le port prallèle du PC.
    Dans cette classe, je forme des "paquets" que j'envoie au port //.
    Bon bref, tout ça pour dire que dans ces méthodes, j'ai besoin de faire de courtes pauses (environ 100 µs), grâce à la fonction wxMicroSleep () : en gros, un "0" dure plus longtemps qu'un "1".
    Bref, toute cette petite cuisine fonctionne parfaitement.

    Mais, les soucis arrivent :
    je souhaite maintenant intégrer cette classe Com à une interface graphique.
    Disons, la classe Fenetre.
    Cette classe Fenetre est toute simple : une fenêtre et un bouton.
    Quand je clique sur le bouton, j'appelle la méthode
    Ca marche, mais ça freeze l'interface durant l'envoi du paquet au port //.

    Ma question : comment éviter ça ?

    je sens bien qu'il faut utiliser les Threads, mais je n'arrive pas à voir comment.
    Je me suis documenté du mieux que j'ai pu sur wxThread, mais je n'y pas.

    Merci de votre aide, je nage !

    Alexis
    Alexis
    ----------
    Delphi 7 Perso [FR]
    Windows XP Pro [FR]

  2. #2
    Membre averti Avatar de wxXav
    Homme Profil pro
    Développeur amateur
    Inscrit en
    Décembre 2008
    Messages
    214
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur amateur

    Informations forums :
    Inscription : Décembre 2008
    Messages : 214
    Points : 354
    Points
    354
    Par défaut
    Salut.

    Pour le principe, voici ce qu'il faut faire :
    • Quand tu cliques sur le bouton, ça appelle une méthode membre de la classe "fenetre"
    • Dans cette méthode membre, tu vas créer une instance d'un classe dérivée de wxThread, et tu vas en lancer l'exécution
    • C'est lors de l'éxécution du thread que tu vas appeler "com.envoyerPaquet()"
    • Il faut penser, lorsque le paquet est envoyé, à prévenir la fenêtre que le thread est terminé (en utilisant un event personnalisé, par exemple).


    Voilà pour ce qui est du principe.
    Si tu as besoin de plus d'infos, n'hésites pas

    @+
    Xav'

  3. #3
    agh
    agh est déconnecté
    Membre du Club
    Inscrit en
    Juillet 2002
    Messages
    51
    Détails du profil
    Informations forums :
    Inscription : Juillet 2002
    Messages : 51
    Points : 49
    Points
    49
    Par défaut
    Bonjour,

    Merci de votre réponse.
    J'ai du mal.
    j'ai essayé de mettre en place ce que vous me dites.

    En fait, l'objet COM et la fenêtre vont "vivre" pendant toute l'execution du programme, puisque durant toute l'execution des paquets vont être envoyés en continu sur le port //.

    Je viens de Java (et oui !) et, en Java, en créant l'objet fenêtre dans un thread "Runnable", et bien tous les traitements lourds, qui sont appelés directement depuis l'IHM n'interfèrent pas sur la fluidité de cette dernière.

    Pourriez-vous me donner un petite exemple ?

    Je vous remercie
    Alexis
    ----------
    Delphi 7 Perso [FR]
    Windows XP Pro [FR]

  4. #4
    Membre averti Avatar de wxXav
    Homme Profil pro
    Développeur amateur
    Inscrit en
    Décembre 2008
    Messages
    214
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur amateur

    Informations forums :
    Inscription : Décembre 2008
    Messages : 214
    Points : 354
    Points
    354
    Par défaut
    Je viens de penser à un truc plus simple qui peut éventuellement résoudre ton problème.
    Si j'ai bien compris, tu as toi même créé la classe COM, et donc la méthode d'envoi/réception des données.
    Tu peux peut-être tout simplement essayer d'insérer un petit wxTheApp->Yield() dans la boucle de traitement des données, ce qui permettra à la frame de se refraichir de temps en temps.

    @+
    Xav'

  5. #5
    agh
    agh est déconnecté
    Membre du Club
    Inscrit en
    Juillet 2002
    Messages
    51
    Détails du profil
    Informations forums :
    Inscription : Juillet 2002
    Messages : 51
    Points : 49
    Points
    49
    Par défaut
    Je vais essayer ça et vous tiens au courant !

    Alexis
    Alexis
    ----------
    Delphi 7 Perso [FR]
    Windows XP Pro [FR]

  6. #6
    agh
    agh est déconnecté
    Membre du Club
    Inscrit en
    Juillet 2002
    Messages
    51
    Détails du profil
    Informations forums :
    Inscription : Juillet 2002
    Messages : 51
    Points : 49
    Points
    49
    Par défaut
    C'est mieux, mais c'est pas encore ça.
    J'explique :

    Quand je clique sur le bouton, le code de l'évenement déclenché est celui là :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    void Fenetre::OnClick (wxCommandEvent& evt)
    {
       this->com.paquetVide () ;
       wxMessageBox (wxT("Envoi d'un paquet idle"), wxT("Info"), wxOK | wxICON_INFORMATION, this) ;
    }
    Donc, quand je clique, l'application ne freeze plus, le paquet part, mais, mais... le wxMessageBox n'apparaît qu'après l'envoi du paquet.
    C'est qui est logique en fait.
    Il faudrait que :
    1) le paquet partent et vive sa vie (en vrai, il y aura en permanence des paquets qui partiront (soit ils seront vides, soit ils contiendront de l'info)
    2) en même temps, au même moment, le MessageBox doit apparaître...

    Je nage vraiment.
    Autant je trouvais les Thread pas trop complexe en Java, autant là... j'avoue, je n'y comprends rien
    Alexis
    ----------
    Delphi 7 Perso [FR]
    Windows XP Pro [FR]

  7. #7
    Membre averti Avatar de wxXav
    Homme Profil pro
    Développeur amateur
    Inscrit en
    Décembre 2008
    Messages
    214
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur amateur

    Informations forums :
    Inscription : Décembre 2008
    Messages : 214
    Points : 354
    Points
    354
    Par défaut
    Effectivement, c'est tout à fait normal que la MessageBox s'affiche quand le paquet est envoyé.

    Il va donc falloir passer par les threads.

    Voici donc le principe de fonctionnement :
    Lorsque tu démarres ton application, elle est éxécutée par un seul thread : le thread principal.
    Il ne peut donc effectuer qu'une seule action à la fois.
    Dans ton cas, il te faut donc un thread secondaire qui va se charger d'envoyer/recevoir les paquets.

    Pour le créer, il faut :
    • Créer une classer dérivée de wxThread. Cette classe devra au minimum surcharger la méthode "Entry" qui est la méthode appelée quand on demande l'exécution du thread. Et si j'ai bien compris ce que tu veux faire, cette fonction contiendra une boucle qui enverra en continu des paquets (vides ou non).
    • Ensuite, il faut pouvoir indiquer au thread secondaire qu'un paquet est disponible à l'envoi. A mon avis, il faut faire l'inverse : il faut que le thread secondaire demande au thread principal s'il n'y a pas quelque chose à envoyer, et si ce n'est pas le cas, il envoie un paquet vide. Pour cette "demande", tu peux par exemple utiliser un wxArray.
    • Si un paquet est disponible, le thread secondaire se l'approprie, et l'enlève du wxArray.
    • Quand tu veux envoyer un paquet (clic sur ton bouton) : tu ajoutes ce paquet au wxArray, et le tour est joué.

    Le seul point à ne pas oublier avec cette méthode (deux threads qui utilisent une zone mémoire "commune", c'est d'utiliser wxMutexGuiEnter et wxMutexGuiLeave depuis le thread secondaire lorsqu'il récupère les données, de façon à supprimer le risque de modifier le wxArray depuis les deux threads en même temps.

    Je vais essayer de te faire un petit exemple assez simple pour que tu puisse voir le principe.

    @+
    Xav'

  8. #8
    agh
    agh est déconnecté
    Membre du Club
    Inscrit en
    Juillet 2002
    Messages
    51
    Détails du profil
    Informations forums :
    Inscription : Juillet 2002
    Messages : 51
    Points : 49
    Points
    49
    Par défaut
    Merci beaucoup !!!
    J'attends votre exemple avec la plus grande impatience ! Là, je sature, ça fait 3 jours que je suis dessus !

    Alexis
    Alexis
    ----------
    Delphi 7 Perso [FR]
    Windows XP Pro [FR]

  9. #9
    Membre averti Avatar de wxXav
    Homme Profil pro
    Développeur amateur
    Inscrit en
    Décembre 2008
    Messages
    214
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur amateur

    Informations forums :
    Inscription : Décembre 2008
    Messages : 214
    Points : 354
    Points
    354
    Par défaut
    Bon, alors voici l'exemple promis.

    J'ai simplifié au maximum le code, alors attention : il n'y a pas de vérification lors de la fermeture de l'application si le thread est lancé.
    Il faut donc bien arrêter le thread avant de fermer la fenêtre.
    Il faut également compiler cet exemple en mode "console", var la simulation de l'envoi des paquets est faite par l'affichage de texte sur la sortie stdout via wxPuts / wxPrintf

    Fichier MainFrame.h :
    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
    #ifndef MAINFRAME_H_INCLUDED
    #define MAINFRAME_H_INCLUDED
     
    #include <wx/wx.h>
     
    #include "mainframe.h"
    class MyThread;
     
    class MainFrame: public wxFrame
    {
        public:
            MainFrame(wxFrame *frame, const wxString& title);
            ~MainFrame();
    		wxString GetStringToSend();
        private:
            void OnSendStringClicked(wxCommandEvent &event);
            void OnStartStopClicked(wxCommandEvent &event);
            void OnTextChanged(wxCommandEvent &event);
            void OnThreadEnded(wxCommandEvent &event);
            wxArrayString m_values;
            wxTextCtrl *m_txtValue;
            wxButton *m_btnStartStop,*m_btnSend;
            MyThread *m_thread;
    };
     
    #endif // MAINFRAME_H_INCLUDED
    Fichier MainFrame.cpp :
    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
    72
    73
    74
    75
    #include "mainframe.h"
    #include "mythread.h"
     
     
    MainFrame::MainFrame(wxFrame *frame, const wxString& title)
        : wxFrame(frame, -1, title)
    {
    	m_thread=NULL;
     
    	wxPanel *mainpanel=new wxPanel(this, -1);
    	wxBoxSizer *mainsizer=new wxBoxSizer(wxVERTICAL);
    		m_btnStartStop=new wxButton(mainpanel, -1, _T("Start communication thread"));
    		mainsizer->Add(m_btnStartStop, 0, wxALL, 5);
    		wxBoxSizer *hsizer=new wxBoxSizer(wxHORIZONTAL);
    			m_txtValue=new wxTextCtrl(mainpanel, -1, _T("Enter text here..."));
    			hsizer->Add(m_txtValue, 1, wxALL|wxALIGN_CENTER_VERTICAL, 0);
    			m_btnSend=new wxButton(mainpanel, -1, _T("Send"));
    			m_btnSend->Disable();
    			hsizer->Add(m_btnSend, 0, wxLEFT|wxALIGN_CENTER_VERTICAL, 5);
    		mainsizer->Add(hsizer, 0, wxALL|wxEXPAND, 5);
    	mainpanel->SetSizer(mainsizer);
     
    	Connect(m_btnStartStop->GetId(), wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(MainFrame::OnStartStopClicked));
    	Connect(m_btnSend->GetId(), wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(MainFrame::OnSendStringClicked));
    	Connect(wxEVT_COMMAND_TEXT_UPDATED , wxCommandEventHandler(MainFrame::OnTextChanged));
    	Connect(wxEVT_MYTHREAD_ENDED, wxCommandEventHandler(MainFrame::OnThreadEnded));
     
    }
     
     
    MainFrame::~MainFrame()
    {
    }
     
    void MainFrame::OnTextChanged(wxCommandEvent &event)
    {
    	if ((m_txtValue->IsEmpty()) || (m_thread==NULL))
    		m_btnSend->Disable();
    	else
    		m_btnSend->Enable();
    }
     
    void MainFrame::OnSendStringClicked(wxCommandEvent &event)
    {
    	m_values.Add(m_txtValue->GetValue());
    	m_txtValue->SetValue(wxEmptyString);
    }
     
    void MainFrame::OnStartStopClicked(wxCommandEvent &event)
    {
    	if (m_thread==NULL)
    	{
    		m_thread=new MyThread(this);
    		m_thread->Create();
    		m_thread->Run();
    		m_btnStartStop->SetLabel(_T("Stop communication thread"));
    		if (!m_txtValue->IsEmpty()) m_btnSend->Enable();
    	} else {
    		m_thread->Delete();
    	}
    }
    void MainFrame::OnThreadEnded(wxCommandEvent &event)
    {
    	m_thread=NULL;
    	m_btnStartStop->SetLabel(_T("Start communication thread"));
    	m_btnSend->Disable();
    }
     
    wxString MainFrame::GetStringToSend()
    {
    	if (m_values.IsEmpty()) return wxEmptyString;
    	wxString  sVal=m_values[0];
    	m_values.RemoveAt(0,1);
    	return sVal;
    }
    Fichier MyThread.h :
    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
    #ifndef MYTHREAD_H_INCLUDED
    #define MYTHREAD_H_INCLUDED
     
    #include <wx/thread.h>
    #include "mainframe.h"
     
    // Le nouveau type permettant à un wxThread de signaler qu'il a terminé son boulot
    extern const wxEventType wxEVT_MYTHREAD_ENDED;
     
    // La macro permettant d'utiliser le type wxEVT_MYTHREAD_ENDED dans une table classique
    #define EVT_MYTHREAD_ENDED(id, fn) \
        DECLARE_EVENT_TABLE_ENTRY( wxEVT_MYTHREAD_ENDED, id, wxID_ANY, \
        (wxObjectEventFunction)(wxEventFunction) wxStaticCastEvent( wxCommandEventFunction, &fn ), \
        (wxObject *) NULL ),
     
    class MyThread : public wxThread
    {
    	public:
    		MyThread(MainFrame *parent);
    		virtual ~MyThread();
    		virtual void* Entry();
    	protected:
    	private:
    		MainFrame *m_frame;
    };
     
    #endif // MYTHREAD_H_INCLUDED
    Fichier MyThread.cpp :
    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
    #include "mythread.h"
     
    const wxEventType wxEVT_MYTHREAD_ENDED = wxNewEventType();
     
    MyThread::MyThread(MainFrame *parent)
    {
    	m_frame=parent;
    	wxPuts(_T("Communication thread started..."));
    }
     
    MyThread::~MyThread()
    {
    	wxPuts(_T("\nCommunication thread destroyed..."));
    }
     
    void* MyThread::Entry()
    {
    	wxPuts(_T("Entering thread's working function."));
    	wxCommandEvent evt(wxEVT_MYTHREAD_ENDED, GetId());
    	wxString sValue;
    	bool bContinue=true;
    	while (bContinue)
    	{
    		wxMilliSleep(250);
    		Yield();
    		wxMutexGuiEnter();
    		sValue=m_frame->GetStringToSend();
    		wxMutexGuiLeave();
    		if (TestDestroy()) bContinue=false;
    		if ((sValue!=wxEmptyString)&&(bContinue))
    		{
    			wxPrintf(_T("\n>>"));
    			for (int i=0;(i<(int)sValue.Length()) && bContinue; i++ )
    			{
    				wxPrintf(_T("%c"),sValue[i]);
    				wxMilliSleep(250);
    				if (TestDestroy()) bContinue=false;
    			}
    		}
    	}
        m_frame->GetEventHandler()->AddPendingEvent(evt);
        wxPuts(_T("\nExiting thread's working function."));
    	return NULL;
    }
    Et voici également une archive 7zip contenant les fichiers sources ainsi que l'exécutable compilé (normalement, il n'y a pas de problème, mais attention quand même aux virus avec l'exécutable).
    Pour l'utiliser :
    • Démarrer le thread avec le bouton correspondant
    • Saisir du texte dans la zone prévue à cet effet
    • Cliquer sur le bouton "Send" pour ajouter le texte dans la pile des "paquets" que le thread doit traiter
    • Le thread va alors récupérer le texte entrée par entrée, et l'afficher dans la fenêtre console caractère par caractère
    • Couper le thread avec le bouton qui a servi à le démarrer (et qui normalement a du changer de label)
    • Fermer l'application


    Si tu as des questions, n'hésites pas (je ne suis pas très fortiche pour commenter mon code ).
    @+
    xav'

  10. #10
    agh
    agh est déconnecté
    Membre du Club
    Inscrit en
    Juillet 2002
    Messages
    51
    Détails du profil
    Informations forums :
    Inscription : Juillet 2002
    Messages : 51
    Points : 49
    Points
    49
    Par défaut
    Merci ! Merci beaucoup !!
    C'est génial.
    J'ai regardé votre exemple, (l'exe surtout) et c'est EXACTEMENT ça que je veux !
    Je vais prendre ma douche, et je m'y attèle.
    Objectif : ce soir il faut que ça marche !

    Je vous tiens au courant.
    Merci encore.

    Pendant que j'y suis, question subsidiaire : actuellement, je gère le port parallèle avec les fonctions du système (différentes sous windows et sous Linux).
    Je n'ai pas trouvé dans la doc de wxWidgets de classes permettant la gestion du port //.. Est-ce que c'est parce que j'ai mal cherché ?

    Je vous remercie encore 1000 fois
    Alexis
    ----------
    Delphi 7 Perso [FR]
    Windows XP Pro [FR]

  11. #11
    Membre averti Avatar de wxXav
    Homme Profil pro
    Développeur amateur
    Inscrit en
    Décembre 2008
    Messages
    214
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur amateur

    Informations forums :
    Inscription : Décembre 2008
    Messages : 214
    Points : 354
    Points
    354
    Par défaut
    Citation Envoyé par agh Voir le message
    Je n'ai pas trouvé dans la doc de wxWidgets de classes permettant la gestion du port //.. Est-ce que c'est parce que j'ai mal cherché ?
    Non, il n'y a rien dans les libs standards pour cela.
    Il existe bien un composant additionnel : wxCtb, mais je ne suis pas certain qu'il prenne en charge le port parallèle.

    Bonne continuation

    @+
    Xav'

  12. #12
    agh
    agh est déconnecté
    Membre du Club
    Inscrit en
    Juillet 2002
    Messages
    51
    Détails du profil
    Informations forums :
    Inscription : Juillet 2002
    Messages : 51
    Points : 49
    Points
    49
    Par défaut
    Bonsoir,

    Je tenais vraiment à vous remercier.
    Ca y est, ça marche !
    Il m'a fallu un peu de temps pour adapter tout ça à mon projet, mais c'est bon.

    Merci encore !
    Alexis
    ----------
    Delphi 7 Perso [FR]
    Windows XP Pro [FR]

  13. #13
    Membre averti Avatar de wxXav
    Homme Profil pro
    Développeur amateur
    Inscrit en
    Décembre 2008
    Messages
    214
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur amateur

    Informations forums :
    Inscription : Décembre 2008
    Messages : 214
    Points : 354
    Points
    354
    Par défaut
    Citation Envoyé par agh Voir le message
    Je tenais vraiment à vous remercier.
    Ca y est, ça marche !
    Il m'a fallu un peu de temps pour adapter tout ça à mon projet, mais c'est bon.

    Merci encore !
    De rien.
    C'est de toute façon en se confrontant à des problèmes concrets de ce style qu'on arrive à progresser.

    Heu, par contre, le vouvoiement ne pourrait-il céder la place à son confrère plus convivial, le tutoiement ?

    Bonne continuation, et n'hésites pas si tu as un soucis.
    Et peut-être à bientôt sur www.wxdev.fr

    @+
    Xav'

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

Discussions similaires

  1. Interruption d'un Thread pendant sleep
    Par abdessalem10 dans le forum Concurrence et multi-thread
    Réponses: 7
    Dernier message: 11/10/2011, 14h04
  2. [VB2005] Thread and Sleep
    Par Kropernic dans le forum Windows Forms
    Réponses: 6
    Dernier message: 25/06/2008, 16h34
  3. threads et sleep
    Par buzzz dans le forum Windows
    Réponses: 2
    Dernier message: 19/02/2005, 01h54
  4. [Thread] besoin sleep(delay)... car java mobilise CPU
    Par thierry198 dans le forum Concurrence et multi-thread
    Réponses: 5
    Dernier message: 27/09/2004, 17h46
  5. [Thread]sleep()
    Par jokoss dans le forum Concurrence et multi-thread
    Réponses: 15
    Dernier message: 03/07/2004, 14h33

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