Ich bin neu in den Aufgaben von .Net 4.0 und konnte nicht finden, was ich für einen aufgabenbasierten Ersatz oder die Implementierung eines Timers hielt, z. B. eine periodische Aufgabe. Gibt es so etwas?
Update Ich habe eine Lösung für meine Anforderungen gefunden, die darin besteht, die "Timer" -Funktionalität in eine Aufgabe mit untergeordneten Aufgaben zu packen, die alle das CancellationToken nutzen, und die Aufgabe zurückzugeben, um an weiteren Aufgabenschritten teilnehmen zu können.
public static Task StartPeriodicTask(Action action, int intervalInMilliseconds, int delayInMilliseconds, CancellationToken cancelToken)
{
Action wrapperAction = () =>
{
if (cancelToken.IsCancellationRequested) { return; }
action();
};
Action mainAction = () =>
{
TaskCreationOptions attachedToParent = TaskCreationOptions.AttachedToParent;
if (cancelToken.IsCancellationRequested) { return; }
if (delayInMilliseconds > 0)
Thread.Sleep(delayInMilliseconds);
while (true)
{
if (cancelToken.IsCancellationRequested) { break; }
Task.Factory.StartNew(wrapperAction, cancelToken, attachedToParent, TaskScheduler.Current);
if (cancelToken.IsCancellationRequested || intervalInMilliseconds == Timeout.Infinite) { break; }
Thread.Sleep(intervalInMilliseconds);
}
};
return Task.Factory.StartNew(mainAction, cancelToken);
}
Antworten:
Es hängt von 4.5 ab, aber das funktioniert.
Natürlich können Sie eine generische Version hinzufügen, die auch Argumente akzeptiert. Dies ähnelt tatsächlich anderen vorgeschlagenen Ansätzen, da Task.Delay unter der Haube einen Timer-Ablauf als Quelle für den Abschluss der Aufgabe verwendet.
quelle
action()
mit einer Wiederholung von an!cancelToken.IsCancellationRequested
. Das ist besser, oder?action
Ausführungszeit". Habe ich recht?UPDATE Ich markiere die Antwort unten als "Antwort", da diese jetzt alt genug ist, um das asynchrone / wartende Muster zu verwenden. Sie müssen dies nicht mehr ablehnen. LOL
Wie Amy antwortete, gibt es keine Tasked-basierte periodische / Timer-Implementierung. Basierend auf meinem ursprünglichen UPDATE haben wir dies jedoch zu etwas sehr Nützlichem entwickelt und die Produktion getestet. Ich dachte, ich würde teilen:
Ausgabe:
quelle
TaskScheduler.FromCurrentSynchronizationContext()
vor dem Einstellen aufrufenmainAction
. Ich übergebe dann den resultierenden Scheduler,MainPeriodicTaskAction
damit er dassubTask
mit erstellt.Es ist nicht genau in
System.Threading.Tasks
, aberObservable.Timer
(oder einfacherObservable.Interval
) aus der Reactive Extensions-Bibliothek ist wahrscheinlich das, wonach Sie suchen.quelle
Bisher habe ich anstelle des Threading-Timers eine LongRunning-TPL-Task für zyklische CPU-gebundene Hintergrundarbeiten verwendet, weil:
Die TPL-Lösung beansprucht jedoch immer einen dedizierten Thread, der nicht erforderlich ist, während auf die nächste Aktion gewartet wird (was meistens der Fall ist). Ich möchte die vorgeschlagene Lösung von Jeff verwenden, um CPU-gebundene zyklische Arbeit im Hintergrund auszuführen, da nur dann ein Threadpool-Thread benötigt wird, wenn Arbeit zu erledigen ist, die für die Skalierbarkeit besser ist (insbesondere wenn die Intervallperiode groß ist).
Um dies zu erreichen, würde ich 4 Anpassungen vorschlagen:
ConfigureAwait(false)
,Task.Delay()
um diedoWork
Aktion ansonsten für einen Thread-Pool-Thread auszuführendoWork
wird sie für den aufrufenden Thread ausgeführt, was nicht die Idee der Parallelität istdoWork
, damit es die Aufgabe abbrechen kannZu Punkt 2 Ich bin mir nicht sicher, ob für das asynchrone Warten noch die TaskCanceledExecption erforderlich ist, oder handelt es sich nur um eine bewährte Methode?
Bitte geben Sie Ihre Kommentare zu der vorgeschlagenen Lösung ...
Update 30.08.2016
Die obige Lösung ruft nicht sofort auf,
doWork()
sondern beginnt mitawait Task.Delay().ConfigureAwait(false)
dem Erreichen des Thread-Wechsels fürdoWork()
. Die folgende Lösung überwindet dieses Problem, indem der erstedoWork()
Anruf in a eingeschlossenTask.Run()
und abgewartet wird.Im Folgenden finden Sie die verbesserte asynchrone \ wait-Ersetzung
Threading.Timer
, die abbrechbare zyklische Arbeiten ausführt und skalierbar ist (im Vergleich zur TPL-Lösung), da sie beim Warten auf die nächste Aktion keinen Thread belegt.Beachten Sie, dass im Gegensatz zum Timer die Wartezeit (
period
) konstant ist und nicht die Zykluszeit. Die Zykluszeit ist die Summe der Wartezeit und deren DauerdoWork()
variieren kann.quelle
ConfigureAwait(false)
wird die Fortsetzung der Methode für den Thread-Pool geplant, sodass der zweite Punkt des Threading-Timers nicht wirklich gelöst wird. Ich denke auch nicht, dasstaskState
es notwendig ist; Die Erfassung von Lambda-Variablen ist flexibler und typsicherer.await Task.Delay()
unddoWork()
sodoWork()
würde sofort während des Startvorgangs ausgeführt werden . Aber ohne einen TrickdoWork()
würde der aufrufende Thread das erste Mal ausgeführt und blockiert. Stephen, hast du eine Lösung für dieses Problem?Task.Run
.Ich musste die wiederkehrenden asynchronen Aufgaben von einer synchronen Methode auslösen.
Dies ist eine Adaption von Jeffs Antwort. Es wird geändert, um a aufzunehmen.
Func<Task>
Außerdem wird sichergestellt, dass der Zeitraum wie oft ausgeführt wird, indem die Laufzeit der Aufgabe von dem Zeitraum für die nächste Verzögerung abgezogen wird.quelle
Ich bin auf ein ähnliches Problem
TaskTimer
gestoßen und habe eine Klasse geschrieben, die eine Reihe von Aufgaben zurückgibt, die auf dem Timer ausgeführt werden: https://github.com/ikriv/tasktimer/ .quelle
Einfach...
quelle