web-dev-qa-db-fra.com

Comment utiliser une minuterie pour attendre?

J'essaie de retarder des événements dans ma méthode en utilisant une minuterie, mais je ne comprends pas nécessairement comment utiliser une minuterie pour attendre.

J'ai configuré mon chronomètre sur 2 secondes, mais lorsque je lance ce code, le dernier appel est exécuté sans délai de 2 secondes.

Timer timer = new Timer();
timer.Tick += new EventHandler(timer_Tick); // Everytime timer ticks, timer_Tick will be called
timer.Interval = (1000) * (2);              // Timer will tick evert second
timer.Enabled = true;                       // Enable the timer


void timer_Tick(object sender, EventArgs e)
{
    timer.Stop();
}

private void button1_Click(object sender, EventArgs e)
{
    label1.Text = "first";
    timer.Start();
    label1.Text = "second";
}

Ainsi, lorsque je clique sur mon bouton, label1 apparaît immédiatement comme "seconde", par opposition à changer pour "premier", attendre 2 secondes, puis changer pour "deuxième". J'ai lu beaucoup de discussions ici sur l'utilisation de minuteries au lieu de thread.sleep, mais je n'arrive pas à trouver/comprendre comment implémenter cela.

12
Fuzz Evans

timer.Start() démarre simplement la minuterie mais revient immédiatement lorsque la minuterie est en arrière-plan. Donc, entre définir le texte de l'étiquette sur first et sur second, il n'y a presque pas de pause. Ce que vous voulez faire, c'est attendre que le chronomètre coche et ensuite seulement mettre à jour l'étiquette à nouveau:

void timer_Tick(object sender, EventArgs e)
{
    timer.Stop();
    label1.Text = "second";
}

private void button1_Click(object sender, EventArgs e)
{
    label1.Text = "first";
    timer.Start();
}

Btw. vous ne devez pas définir timer.Enabled sur true, vous démarrez déjà le chronomètre avec timer.Start().

Comme mentionné dans les commentaires, vous pouvez placer la création du timer dans une méthode, comme ceci (note: cela n’a pas été testé):

public void Delayed(int delay, Action action)
{
    Timer timer = new Timer();
    timer.Interval = delay;
    timer.Tick += (s, e) => {
        action();
        timer.Stop();
    };
    timer.Start();
}

Et ensuite, vous pourriez simplement l'utiliser comme ceci:

private void button1_Click(object sender, EventArgs e)
{
    label1.Text = "first";
    Delayed(2000, () => label1.Text = "second");
}

Suivi de Tergiver

Est-ce que l'utilisation retardée contient une fuite de mémoire (fuite de référence)?

L'abonnement à un événement crée toujours une référence à double sens.

Dans ce cas, timer.Tick obtient une référence à une fonction anonyme (lambda). Cette fonction soulève une variable locale timer, bien qu’il s’agisse d’une référence et non d’une valeur, et contient une référence au délégué passé dans Action. Ce délégué va contenir une référence à label1, un membre d'instance de Form. Donc, y a-t-il une référence circulaire de Timer à Form?

Je ne connais pas la réponse, je trouve difficile de raisonner. Parce que je ne sais pas, je supprimerais l'utilisation de lambda dans Delayed, ce qui en ferait une méthode appropriée. De plus, en plus d'arrêter le chronomètre (qui est le paramètre sender de la méthode), également supprimer l'événement.

Les lambdas ne posent généralement pas de problèmes pour la collecte des ordures. Dans ce cas, l'instance de minuterie n'existe que localement et la référence dans le lambda n'empêche pas le garbage collection de collecter les instances (voir aussi cette question ).

En fait, j'ai de nouveau testé cela à l'aide du .NET Memory Profiler. Les objets de la minuterie ont été bien rassemblés et aucune fuite n’est survenue. Le profileur m'a toutefois averti qu'il y avait des cas où «[…] ont été ramassés sans avoir été correctement éliminés». Supprimer le gestionnaire d'événements en lui-même (en y gardant une référence) n'a pas résolu le problème. Changer la référence de la minuterie capturée en (Timer)s ne l’a pas changé non plus.

Évidemment, ce qui a aidé, c’est d’appeler une timer.Dispose() dans le gestionnaire d’événements après l’arrêt du minuteur, mais j’arguais si cela était vraiment nécessaire. Je ne pense pas que l’avertisseur/la note du profileur soit aussi critique.

11
poke

Si vous utilisez C # 5.0 await rend ceci beaucoup plus facile:

private async void button1_Click(object sender, EventArgs e)
{
    label1.Text = "first";
    await Task.Delay(2000);
    label1.Text = "second";
}
7
Servy
       private bool Delay(int millisecond)       
       {

           Stopwatch sw = new Stopwatch();
           sw.Start();
           bool flag = false;
           while (!flag)
           {
               if (sw.ElapsedMilliseconds > millisecond) 
               {
                  flag = true;
               }
           }
           sw.Stop();
           return true;

       }

        bool del = Delay(1000);
0
Leon

Si tout ce que vous essayez de faire est de changer le texte lorsque le minuteur sonne, ne feriez-vous pas mieux de mettre ...

label1.Text = "second";

... Dans la coche de la minuterie, avant ou après que vous changiez la minuterie en enabled = false;

Ainsi;

void timer_Tick(object sender, EventArgs e)
{
  timer.Stop();
  label1.Text = "second";
}

private void button1_Click(object sender, EventArgs e)
{
  label1.Text = "first";
  timer.Start();
}
0
Shane.C