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:
- la tâche TPL prend en charge l'annulation
- le minuteur de thread peut démarrer un autre thread pendant que le programme s'arrête, ce qui peut causer des problèmes avec les ressources supprimées
- chance de dépassement: le minuteur de thread pourrait démarrer un autre thread alors que le précédent est toujours en cours de traitement en raison d'un long travail inattendu (je sais, cela peut être évité en arrêtant et en redémarrant le minuteur)
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:
- Ajouter
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élisme
- Tenez-vous en au modèle d'annulation en lançant une TaskCanceledException (toujours requise?)
- Transférer le jeton d'annulation à
doWork
pour lui permettre d'annuler la tâche
- Ajouter un paramètre de type objet pour fournir des informations sur l'état de la tâche (comme une tâche TPL)
À 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?
public static async Task Run(Action<object, CancellationToken> doWork, object taskState, TimeSpan period, CancellationToken cancellationToken)
{
do
{
await Task.Delay(period, cancellationToken).ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();
doWork(taskState, cancellationToken);
}
while (true);
}
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 par await Task.Delay().ConfigureAwait(false)
obtenir le commutateur de thread pour doWork()
. La solution ci-dessous résout ce problème en enveloppant le premier doWork()
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ée doWork()
peut varier.
public static async Task Run(Action<object, CancellationToken> doWork, object taskState, TimeSpan period, CancellationToken cancellationToken)
{
await Task.Run(() => doWork(taskState, cancellationToken), cancellationToken).ConfigureAwait(false);
do
{
await Task.Delay(period, cancellationToken).ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();
doWork(taskState, cancellationToken);
}
while (true);
}