wake-up-neo.com

Wie verwende ich einen Timer zum Warten?

Ich versuche, Ereignisse in meiner Methode mithilfe eines Timers zu verzögern. Allerdings verstehe ich nicht unbedingt, wie ein Timer zum Warten verwendet wird.

Ich habe meinen Timer auf 2 Sekunden eingestellt, aber wenn ich diesen Code ausführt, wird der letzte Anruf ohne Verzögerung von 2 Sekunden ausgeführt.

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";
}

Wenn ich also auf meine Schaltfläche klicke, wird label1 sofort als "Sekunde" angezeigt, anstatt auf "Erste" zu wechseln, 2 Sekunden zu warten und dann auf "Sekunde" zu wechseln. Ich habe hier viele Threads über die Verwendung von Timern anstelle von thread.sleep gelesen, aber ich kann nicht herausfinden, wie ich das tatsächlich implementieren kann.

12
Fuzz Evans

timer.Start() startet nur den Timer, kehrt jedoch sofort zurück, während der Timer im Hintergrund läuft. Zwischen dem Setzen des Beschriftungstextes auf first und second gibt es also fast keine Pause. Sie möchten warten, bis der Timer läuft und erst dann das Etikett erneut aktualisieren:

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();
}

Übrigens Sie sollten timer.Enabled nicht auf true setzen, Sie starten den Timer bereits mit timer.Start().

Wie in den Kommentaren erwähnt, können Sie die Timer-Erstellung in eine Methode wie die folgende einfügen (Hinweis: Dies ist nicht getestet):

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

Und dann könntest du es einfach so verwenden:

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

Tergivers Nachbereitung

Enthält die Verwendung von Delayed ein Speicherverlust (Referenzleck)?

Durch das Abonnieren eines Ereignisses wird immer eine bidirektionale Referenz erstellt.

In diesem Fall erhält timer.Tick eine Referenz auf eine anonyme Funktion (Lambda). Diese Funktion hebt eine lokale Variable timer ab, obwohl es sich um einen Verweis und nicht um einen Wert handelt, der einen Verweis auf den in Action übergebenen Delegaten enthält. Dieser Delegat wird eine Referenz auf label1 enthalten, ein Instanzmitglied der Form. Gibt es also eine Zirkelreferenz von der Timer auf die Form?

Ich kenne die Antwort nicht, es fällt mir schwer, darüber nachzudenken. Da ich nicht weiß, würde ich die Verwendung des Lambda in Delayed entfernen, es zu einer geeigneten Methode machen und neben dem Stoppen des Timers (der der Parameter sender der Methode ist) auch das Ereignis entfernen.

Normalerweise verursachen Lambdas keine Probleme bei der Müllsammlung. In diesem Fall ist die Timer-Instanz nur lokal vorhanden und die Referenz im Lambda verhindert nicht, dass die Garbage Collection die Instanzen sammelt (siehe auch diese Frage ).

Ich habe das tatsächlich noch einmal mit dem .NET Memory Profiler getestet. Die Timer-Objekte wurden gut gesammelt, und es kam kein Lecken vor. Der Profiler hat mich jedoch gewarnt, dass es Fälle gibt, in denen „[…] Müll gesammelt wurde, ohne ordnungsgemäß entsorgt zu werden“. Das Entfernen des Ereignishandlers in sich selbst (durch einen Verweis darauf) hat dies jedoch nicht behoben. Das Ändern des erfassten Zeitgeberverweises auf (Timer)s hat dies auch nicht geändert.

Was natürlich geholfen hat, war, nach dem Stoppen des Timers eine timer.Dispose() im Event-Handler aufzurufen. Ich glaube nicht, dass die Warnung/der Hinweis des Profilers so kritisch ist.

11
poke

Wenn Sie C # 5.0 verwenden, macht await das viel einfacher:

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

Wenn Sie nur versuchen, den Text zu ändern, wenn der Timer tickt, wäre es nicht besser ...

label1.Text = "second";

... Im Timer-Tick vor oder nach dem Ändern des Timers auf enabled = false;

Wie so;

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