Je suis nouveau dans les tâches de .Net 4.0 et je n'ai pas pu trouver ce que je pensais être un remplacement basé sur une tâche ou une implémentation d'un minuteur, par exemple une tâche périodique. Existe-t-il une telle chose?
Mise à jour J'ai trouvé ce que je pense être une solution à mes besoins qui est d'encapsuler la fonctionnalité "Timer" dans une tâche avec des tâches enfants qui profitent toutes du CancellationToken et renvoie la tâche pour pouvoir participer à d'autres étapes de tâche.
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);
}
Réponses:
Cela dépend de 4.5, mais cela fonctionne.
Évidemment, vous pouvez ajouter une version générique qui prend également des arguments. Ceci est en fait similaire à d'autres approches suggérées car sous le capot, Task.Delay utilise une expiration du minuteur comme source d'achèvement de tâche.
la source
action()
avec une répétition de!cancelToken.IsCancellationRequested
. C'est mieux, non?action
exécution", j'ai raison?MISE À JOUR Je marque la réponse ci-dessous comme "réponse" car c'est assez vieux maintenant que nous devrions utiliser le modèle async / wait. Plus besoin de voter contre cela. LOL
Comme Amy a répondu, il n'y a pas d'implémentation périodique / minuterie basée sur les tâches. Cependant, sur la base de ma mise à jour originale, nous avons fait évoluer cela en quelque chose d'assez utile et testé en production. Je pensais que je partagerais:
Production:
la source
TaskScheduler.FromCurrentSynchronizationContext()
avant de configurermainAction
. Je passe ensuite le planificateur résultantMainPeriodicTaskAction
pour qu'il crée lesubTask
avec.Ce n'est pas exactement dans
System.Threading.Tasks
, maisObservable.Timer
(ou plus simpleObservable.Interval
) de la bibliothèque Reactive Extensions est probablement ce que vous recherchez.la source
Jusqu'à présent, j'utilisais une tâche TPL LongRunning pour le travail en arrière-plan lié au processeur cyclique au lieu du minuteur de thread, car:
Cependant, la solution TPL revendique toujours un thread dédié qui n'est pas nécessaire en attendant la prochaine action (ce qui est la plupart du temps). Je voudrais utiliser la solution proposée par Jeff pour effectuer un travail cyclique lié au processeur en arrière-plan car il n'a besoin d'un thread de pool de threads que lorsqu'il y a du travail à faire, ce qui est meilleur pour l'évolutivité (en particulier lorsque la période d'intervalle est grande).
Pour y parvenir, je proposerais 4 adaptations:
ConfigureAwait(false)
àTask.Delay()
pour exécuter l'doWork
action sur un thread de pool de threads, sinondoWork
sera effectué sur le thread appelant ce qui n'est pas l'idée du parallélismedoWork
pour lui permettre d'annuler la tâcheÀ propos du point 2 Je ne suis pas sûr, est-ce que async wait nécessite toujours la TaskCanceledExecption ou est-ce simplement la meilleure pratique?
Veuillez faire part de vos commentaires sur la solution proposée ...
Mise à jour 2016-8-30
La solution ci-dessus n'appelle pas immédiatement
doWork()
mais commence parawait Task.Delay().ConfigureAwait(false)
obtenir le commutateur de thread pourdoWork()
. La solution ci-dessous résout ce problème en enveloppant le premierdoWork()
appel dans unTask.Run()
et en l'attendant.Vous trouverez ci-dessous le remplacement amélioré async \ await pour
Threading.Timer
qui effectue un travail cyclique annulable et est évolutif (par rapport à la solution TPL) car il n'occupe aucun thread en attendant l'action suivante.Notez que contrairement au Timer, le temps d'attente (
period
) est constant et non le temps de cycle; le temps de cycle est la somme du temps d'attente et dont la duréedoWork()
peut varier.la source
ConfigureAwait(false)
planifie la poursuite de la méthode vers le pool de threads, donc cela ne résout pas vraiment le deuxième point concernant le minuteur de thread. Je ne pense pas non plus que cetaskState
soit nécessaire; La capture des variables lambda est plus flexible et sécurisée.await Task.Delay()
etdoWork()
doncdoWork()
s'exécuter immédiatement au démarrage. Mais sans astucedoWork()
, exécuterait le thread appelant la première fois et le bloquerait. Stephen, avez-vous une solution à ce problème?Task.Run
.J'avais besoin de déclencher les tâches asynchrones récurrentes à partir d'une méthode synchrone.
Ceci est une adaptation de la réponse de Jeff. Il est modifié pour prendre un.
Func<Task>
Il s'assure également que la période correspond à la fréquence à laquelle elle est exécutée en déduisant le temps d'exécution de la tâche de la période du délai suivant.la source
J'ai rencontré un problème similaire et j'ai écrit une
TaskTimer
classe qui renvoie une série de tâches qui se terminent sur la minuterie: https://github.com/ikriv/tasktimer/ .la source
Facile...
la source