Frage

Ich habe eine Methode, die für eine bestimmte Zeitspanne verzögert ausgeführt werden sollte.

Sollte ich

Thread thread = new Thread(() => {
    Thread.Sleep(millisecond);
    action();
});
thread.IsBackground = true;
thread.Start();

oder

Timer timer = new Timer(o => action(), null, millisecond, -1);

Ich hatte einige Artikel über Thread.Sleep verwendet, ist schlechtes Design. Aber ich verstehe nicht wirklich, warum.

Aber für die Verwendung von Timer, Timer hat Methode verfügen. Da die Ausführung verzögert wird, weiß ich nicht, wie Timer zu entsorgen. Haben Sie irgendwelche Vorschläge?

Oder wenn Sie alternative Codes für die verzögerte Ausführung sind auch schätzen.

War es hilfreich?

Lösung

Ein Unterschied besteht darin, dass System.Threading.Timer den Rückruf auf einem Thread-Pool-Thread entsendet, anstatt einen neuen Thread jedes Mal zu schaffen. Wenn Sie diese brauchen mehr zu geschehen als einmal während der gesamten Lebensdauer Ihrer Anwendung, wird dies den Aufwand für die Erstellung zu speichern und ein Bündels von Fäden zu zerstören (ein Verfahrens, das ist sehr ressourcenintensiv, da der Artikel, der Sie weist darauf hin, Referenz), da wird es nur wieder verwenden Threads im Pool, und wenn Sie mehr als einen Timer geht an haben, sobald es bedeutet, dass Sie auf einmal weniger Threads ausführen (auch erhebliche Einsparung von Ressourcen).

Mit anderen Worten, Timer wird viel effizienter sein. Es kann auch genauer sein, da Thread.Sleep nur mindestens so lang wie die Höhe der Zeit ist garantiert Sie angeben, warten (das OS kann es viel länger schlafen gelegt). Zugegeben, das ist Timer noch nicht genau genau sein wird, aber die Absicht ist, den Rückruf möglichst nahe an die angegebenen Zeit wie möglich zu schießen, während dies nicht unbedingt die Absicht Thread.Sleep ist.

Wie für die Timer zu zerstören, kann der Rückruf einen Parameter akzeptieren, so können Sie in der Lage sein, den Timer selbst als Parameter zu übergeben und rufen Entsorgen in den Rückruf (obwohl ich nicht versucht haben, diese - ich denke, es möglich ist, dass der Timer kann sich während des Rückrufs gesperrt werden).

Edit:. Nein, ich denke, man kann dies nicht tun, da Sie die Callback-Parameter in dem Timer Konstruktor selbst angeben müssen

Vielleicht so etwas wie das? (Auch hier hat es nicht wirklich versucht)

class TimerState
{
    public Timer Timer;
}

... und den Timer zu starten:

TimerState state = new TimerState();

lock (state)
{
    state.Timer = new Timer((callbackState) => {
        action();
        lock (callbackState) { callbackState.Timer.Dispose(); }
        }, state, millisecond, -1);
}

Die Verriegelung sollte den Timer-Rückruf verhindern versucht, den Timer vor dem Timer Feld frei gesetzt wurde.


Nachtrag: Wie der Kommentator wies darauf hin, wenn action() etwas mit der Benutzeroberfläche des Fall ist, dann ein System.Windows.Forms.Timer verwendet, ist wahrscheinlich eine bessere Wette, da es den Rückruf auf dem UI-Thread ausgeführt wird. Ist dies jedoch nicht der Fall ist, und es ist bis auf Thread.Sleep vs. Threading.Timer, Threading.Timer ist der Weg zu gehen.

Andere Tipps

ThreadPool.RegisterWaitForSingleObject statt Timer:

//Wait 5 seconds then print out to console. 
//You can replace AutoResetEvent with a Semaphore or EventWaitHandle if you want to execute the command on those events and/or the timeout
System.Threading.ThreadPool.RegisterWaitForSingleObject(new AutoResetEvent(false), (state, bTimeout) => Console.WriteLine(state), "This is my state variable", TimeSpan.FromSeconds(5), true);

Ich denke, Thread.Sleep in Ordnung ist, wenn Sie wirklich die Anwendung pausieren für eine bestimmte Menge an Zeit mögen. Ich denke, der Grund, warum die Leute sagen, ist es ein schlechtes Design, weil in den meisten Situationen die Menschen nicht wollen eigentlich die Anwendung pausieren.

Zum Beispiel war ich auf einem POP3-Client arbeiten, wo der Programmierer wurde mit Thread.Sleep (1000) warten, während die Buchse Mail abgerufen werden. In dieser Situation war es besser, einen Event-Handler an die Steckdose und Weiterbildung der Programmausführung, nachdem die Buchse abgeschlossen hatte zu anschließen.

Ich erinnere mich, eine Lösung ähnlich wie Erics einer Implementierung. Dies ist jedoch eine Arbeitsgruppe ein;)

class OneTimer
    {
        // Created by Roy Feintuch 2009
        // Basically we wrap a timer object in order to send itself as a context in order to dispose it after the cb invocation finished. This solves the problem of timer being GCed because going out of context
        public static void DoOneTime(ThreadStart cb, TimeSpan dueTime)
        {
            var td = new TimerDisposer();
            var timer = new Timer(myTdToKill =>
            {
                try
                {
                    cb();
                }
                catch (Exception ex)
                {
                    Trace.WriteLine(string.Format("[DoOneTime] Error occured while invoking delegate. {0}", ex), "[OneTimer]");
                }
                finally
                {
                    ((TimerDisposer)myTdToKill).InternalTimer.Dispose();
                }
            },
                        td, dueTime, TimeSpan.FromMilliseconds(-1));

            td.InternalTimer = timer;
        }
    }

    class TimerDisposer
    {
        public Timer InternalTimer { get; set; }
    }

Das einzige Rindfleisch, das ich mit dem System.Timer ist, dass die meiste Zeit ich es für lange Verzögerungen verwendet gesehen haben (Stunden, Minuten) in Umfragen und Entwickler oft vergessen, die Veranstaltung Vor sie starten sie den Timer. Das bedeutet, dass, wenn ich die App oder einen Dienst zu starten, ich habe bis der Zeitgeber abgelaufen ist, warten (Stunden, Minuten), bevor es tatsächlich ausführt.

Sicher, ist dies kein Problem mit dem Timer, aber ich denke, dass seine oft nicht richtig durch, weil sein nur allzu leicht zu Missbrauch verwendet.

@miniscalope Nein nicht statt Timer verwendet ThreadPool.RegisterWaitForSingleObject wird System.Threading.Timer einen Rückruf Warteschlange auf einem Thread-Pool-Thread ausgeführt werden, wenn die Zeit abgelaufen ist und keine Warte Griff benötigen, warten Sie, einzelnes Objekt wird ein thread~~POS=TRUNC-Thread wartet auf die Veranstaltung binden zu signalisieren oder der Timeout abläuft, bevor der Thread den Rückruf aufruft.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top