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

C# Discussion :

Passer des infos à un thread en pause


Sujet :

C#

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre confirmé
    Homme Profil pro
    Développeur Web
    Inscrit en
    Juin 2010
    Messages
    88
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Juin 2010
    Messages : 88
    Par défaut Passer des infos à un thread en pause
    Bonjour,

    Voici en quelques lignes le projet:
    Il s'agit d'une application windows testant des temps de réactivité de l'utilisateur. Elle émet donc un signal et mesure le temps entre le signal et la réaction de l'utilisateur.

    Je suis parti sur un modèle multithread afin que le code qui lance le signal et attend ne pénalise pas l'interface utilisateur.

    Mon form principal possède un backgroundworker qui lance une classe chargée d’émettre le signal (son) et d'attendre la réaction de l'utilisateur

    J'ai donc le code suivant:

    Dans le form principal
    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
     
            AudioTestsWorker runningTest;
     
            private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
            {
                // This event handler is where the actual work is done.
                // This method runs on the background thread.
     
                // Get the BackgroundWorker object that raised this event.
                System.ComponentModel.BackgroundWorker bgWorker;
                bgWorker = (System.ComponentModel.BackgroundWorker)sender;
     
                // Get the Worker object and call the main method.
                AudioTestsWorker wk = (AudioTestsWorker)e.Argument;
                wk.RunTest(bgWorker, e, 10);
            }
     
            private void StartWorkerThread()
            {
                // Initialize the object that the background worker calls.
                runningTest = new AudioTestsWorker();
     
                // Start the asynchronous operation.
                backgroundWorker1.RunWorkerAsync(runningTest);
            }
            private void Form1_KeyDown(object sender, KeyEventArgs e)
            {
                if (runningTest != null)
                    runningTest.KeyPressed(DateTime.Now);
            }
    Dans ma classe AudioTestsWorker
    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
     
        public class AudioTestsWorker
        {
            protected DateTime _startTime;
            protected DateTime _keyTime;
            protected Boolean _keyPressed;
            protected int[] _results;
     
            public void RunTest(
                    System.ComponentModel.BackgroundWorker worker,
                    System.ComponentModel.DoWorkEventArgs e,
                    int count)
            {
                _results = new int[count];
     
                for (int cpt=0;cpt<count;cpt++) {
                    if (worker.CancellationPending) {
                        e.Cancel = true;
                        break;
                    }
                    else {
                        worker.ReportProgress(0, cpt+1);
                    }
     
                    Random rnd      = new Random(unchecked((int)(DateTime.Now.Ticks)));
                    int waitTime    = rnd.Next(6000);
                    Thread.Sleep(waitTime);
     
                    _keyPressed      = false;
     
                    SystemSounds.Beep.Play();
                    _startTime = DateTime.Now;
     
                    for (int cpt2 = 0; cpt2 < 20; cpt2++)
                    {
                        if (_keyPressed)
                            break;
                        Thread.Sleep(1000);
                    }
     
                    if (!_keyPressed)
                        throw new Exception("Vous n'avez pas appuyé dans les temps");
                    else
                        _results[cpt] = (_keyTime - _startTime).Milliseconds;
     
                }
            }
     
            public void KeyPressed(DateTime keyTime) {
                if (!_keyPressed)
                {
                    _keyPressed = true;
                    _keyTime = keyTime;
                }
            }
     
        }
    Donc le thread enfant lance le son, et attend jusqu'à 20sec pour déclarer qu'on n'a pas réagit à temps.
    Comme le thread enfant est bloqué en sleep, c'est le form principal qui capte le keypress et le passe au thread enfant.

    Donc tout se passe bien, les temps de réaction sont bien mesurés mais... et oui, il y a un mais...

    Au moment où l'utilisateur presse une touche, l'application effet un bip de mécontentement.
    Apres reflexion, je me demande si ce n'est pas la ligne suivante (en fin du mainform):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
                    runningTest.KeyPressed(DateTime.Now);
    Qui rale car j'appelle une méthode sur un thread en sleep et du coup, ça pose probleme

    si c'est bien ça, comment appréhender ce genre de problématique ???

    J'ai pensé à une autre approche:
    Le form principal gére l'emission du signal et en note la date/heure.
    Ce dernier ne se bloque pas en sleep et ne fait rien.
    La méthode keypress note la date/heure de la réaction de l'utilisateur.

    Jusque là tout est beau, aucun thread n'est bloqué et tout devrait bien se passer. Mais il faut mettre en place un timeout et ne pas attendre indéfiniment que l'utilisateur réagisse.
    Du coup, j'ai pensé faire un thread enfant juste pour attendre, avec un sleep(20000)

    Donc le form lance le signal, note la date/heure et déclenche le thread enfant.
    Si une touche est pressée, le form principal coupe le thread enfant, et calcule le temps de réaction
    Si le thread enfant arrive à son temps écoulé, il se ferme et le form, alors déclare qu'il y a eu timeout dans une méthode backgroundWorker1_RunWorkerCompleted

    N'est ce pas mieux ?

    Merci d'avance pour votre aide

  2. #2
    Expert confirmé Avatar de Graffito
    Profil pro
    Inscrit en
    Janvier 2006
    Messages
    5 993
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2006
    Messages : 5 993
    Par défaut
    Utiliser du multithread me semble inutilement compliquer ls choses.

    On crée un timer de 20 seondes pour le TimeOut (enabled=false au départ).

    Après le SystemSounds.Beep.Play(), on "enable" le timer et on active l'event KeyDown.

    Quand un des evénements KeyDown (avec KeyPress) ou timer se produit, on obtient le délai de réaction, on désactive KeyDown, "disable" le Timer et on poursuit l'expérience.

  3. #3
    Membre confirmé
    Homme Profil pro
    Développeur Web
    Inscrit en
    Juin 2010
    Messages
    88
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Juin 2010
    Messages : 88
    Par défaut
    Bonjour,

    Merci pour cette piste.
    J'ai appliqué l'exemple d'ici http://msdn.microsoft.com/fr-fr/library/k0wdddfy.aspx en le modifiant pour mon projet.

    Je me retrouve face à un nouveau problème. En effet, le timer ne fait rien d'autre, si j'ai bien compris, que ce que j'avais manuellement fait, c'est à dire qu'il fonctionne sur un nouveau thread.

    Du coup, la fonction de timeout est appelée non par le thread principal mais par le thread enfant du timer.
    De ce fait, cette fonction ne peut accéder aux objets/variables d'instance du thread principal.

    Par exemple:
    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
     
            private void endTests()
            {
                _running = false;
                Cancel.Enabled = false;
                Start.Enabled = true;
            }
     
            private void beginUnitTest()
            {
                if (!_running)
                    return;
                if (_currentTestNumber >= _totalTest)
                    return;
     
                _currentTestNumber++;
     
                Random rnd = new Random(unchecked((int)(DateTime.Now.Ticks)));
                int waitTime = rnd.Next(6000);
                Thread.Sleep(waitTime);
     
                SystemSounds.Beep.Play();
                _startTime = DateTime.Now;
     
                aTimer = new System.Timers.Timer(10000);
                aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
                aTimer.AutoReset = false;
                aTimer.Enabled = true;
     
            }
            private void OnTimedEvent(object source, ElapsedEventArgs e)
            {
                endTests();
            }
    du coup, je prends une exception à la seconde ligne de endTests car Cancel, qui est un bouton de mon form, a été instancié par le thread principal alors que la méthode endTests est invoquée par le thread du timer....

    Comment faire ?

    encore merci

  4. #4
    Expert confirmé Avatar de Graffito
    Profil pro
    Inscrit en
    Janvier 2006
    Messages
    5 993
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2006
    Messages : 5 993
    Par défaut
    Reprenons le tout (J'ai mis le KeyPress sur une TextBox) :
    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
     
    public partial class Form1 : Form
    {
      private DateTime StartTime ;
      private DateTime EndTime ;
     
      public Form1()
      {
        InitializeComponent();
        timer1.Interval=20000 ;
        EnableEvents(false);
      }
     
      private bool EnableEvents(bool Enable)
      {
         timer1.Enabled=Enable ;
         textBox1.KeyPress-=textBox1_KeyPress ;
         if (Enable) textBox1.KeyPress+=textBox1_KeyPress ;
      }
     
      private void button1_Click(object sender, EventArgs e)
      {
         PlaySound() ;
         StartTime=DateTime.Now ;
         EnableEvents(true) ;
      }
     
      private void timer1_Tick(object sender, EventArgs e)
      {
         EnableEvents(false) ;
         MessageBox.Show("TimeOut") ;
       }
     
      private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
      {
         EndTime=DateTime.Now ;
         EnableEvents(false) ;
         MessageBox.Show("Temps de reaction = "+EndTime.Subtract  (StartTime).ToString()) ;
      }
     
      private void PlaySound()
      {
        // ...
       }
    }

  5. #5
    Membre confirmé
    Homme Profil pro
    Développeur Web
    Inscrit en
    Juin 2010
    Messages
    88
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Juin 2010
    Messages : 88
    Par défaut
    Merci

    J'imagine que c'est une vue partielle du code car je ne vois pas où tu mappes le timer1_Tick sur l'expiration du timer (ni sa déclaration et instanciation d'ailleurs)

    Il y a une partie que je ne comprends pas:
    textBox1_KeyPress-=textBox1.KeyPress ;

    comment fonctionne cette instruction ? Je connaissais le += pour ajouter une fonction sur l'évenement mais ça, je découvre

    encore merci

  6. #6
    Expert confirmé Avatar de Graffito
    Profil pro
    Inscrit en
    Janvier 2006
    Messages
    5 993
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2006
    Messages : 5 993
    Par défaut
    J'ai supposé que l'on mettait le Timer sur la form avec le designer et que l'on créait aussi à partir du Designer l'event Tick.

    L'instruction "-=" est le pendant du "+=" et enlève la procédure de la liste des procédures inscrites dans l'event. Si la procedure n'existe pas dans la liste, il ne se passe rien : pas d'exception.

    Edit : j'ai corrigé l'instruction :
    if (Enable) textBox1.KeyPress+=textBox1_KeyPress ;

Discussions similaires

  1. passer des parametres à un thread (VC++)
    Par me_myself dans le forum Visual C++
    Réponses: 4
    Dernier message: 30/11/2006, 12h24
  2. Faire passer des infos de Word a excel
    Par canaboss dans le forum VB 6 et antérieur
    Réponses: 2
    Dernier message: 24/07/2006, 08h06
  3. [perl][up] passer des references entre thread
    Par zeroox dans le forum Langage
    Réponses: 6
    Dernier message: 29/03/2006, 22h01
  4. Passer des infos d'une zone de liste à une autre
    Par Rinecka dans le forum Access
    Réponses: 1
    Dernier message: 09/01/2006, 11h59
  5. [CR7][VB6] Passer des infos de Vb vers CR
    Par tripper.dim dans le forum SDK
    Réponses: 2
    Dernier message: 13/11/2003, 13h30

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