web-dev-qa-db-fra.com

Comment obtenir une notification de fin d'une System.Threading.Tasks.Task

Je suis actuellement en train de remplacer certaines fonctionnalités de tâches personnalisées par une nouvelle implémentation utilisant la nouvelle fonctionnalité System.Threading.Tasks disponible dans .net 4.

J'ai un petit problème cependant, et bien que je puisse penser à des solutions, je voudrais quelques conseils sur ce qui est généralement la meilleure façon de le faire, et si je manque un truc quelque part.

Ce dont j'ai besoin, c'est qu'un processus arbitraire puisse démarrer une tâche, puis continuer sans attendre la fin de la tâche. Ce n’est pas un problème, mais lorsque j’ai besoin de faire quelque chose avec le résultat d’une tâche, je ne suis pas tout à fait sûr de la meilleure façon de le faire.

Tous les exemples que j'ai vus utilisent Wait () jusqu'à la fin de la tâche ou font référence au paramètre Result de la tâche. Les deux vont bloquer le thread qui a lancé la tâche, ce que je ne veux pas.

Quelques solutions auxquelles j'ai pensé:

  1. Créez un nouveau thread et démarrez la tâche dessus, puis utilisez Wait () ou .Result pour bloquer le nouveau thread et synchroniser le résultat avec l'appelant d'une manière ou d'une autre, éventuellement en interrogeant le paramètre tâches IsCompleted.

  2. Avoir une tâche «Notifier terminé» que je peux démarrer une fois la tâche que je souhaite exécuter, puis déclencher un événement statique ou autre.

  3. Passez un délégué à l’entrée de la tâche et appelez-le pour signaler que la tâche est terminée.

Je peux penser à leurs avantages et à leurs inconvénients, mais je n'aime surtout pas l'idée de créer explicitement un nouveau fil pour démarrer la tâche lorsque l'un des objectifs de l'utilisation de la classe Task est en premier lieu. Abstraire de l'utilisation directe de thread.

Des idées sur la meilleure façon? Est-ce que je manque quelque chose de simple? Un événement 'Terminé' serait-il trop demander :)? (Bien sûr, il y a une bonne raison pour qu'il n'y en ait pas!)

28
Ben Williams

I suspect vous recherchez Task.ContinueWith (ou Task<T>.ContinueWith ). En gros, ils disent: "Lorsque vous avez terminé cette tâche, exécutez cette action". Cependant, vous pouvez spécifier différentes options pour mieux les contrôler.

MSDN donne beaucoup plus de détails à ce sujet dans "Comment: chaîner plusieurs tâches avec des continuations" et "Tâches de continuation" .

33
Jon Skeet

En C # moderne, il n'est plus nécessaire d'appeler explicitement ContinueWith(). Une alternative à la réponse acceptée à l'origine serait de créer simplement une méthode async qui awaits la Task en question et fait ce qu'il veut lorsque la Task sera terminée.

Par exemple, supposons que vous souhaitiez générer un événement appelé TaskCompleted à la fin de la Task. Vous écririez une méthode comme:

async Task RaiseEventWhenTaskCompleted(Task task)
{
    await task;
    TaskCompleted?.Invoke(this, EventArgs.Empty);
}

Pour "enregistrer" l'attente, appelez simplement la méthode ci-dessus. Ajoutez le traitement des exceptions comme vous le souhaitez, soit dans la méthode ci-dessus, soit dans un code qui observera la valeur Task renvoyée par la méthode ci-dessus.

7
Peter Duniho

Vous pouvez appliquer un suite de la tâche .

Task implémente également IAsyncResult afin que vous puissiez utiliser le approches standard pour cette interface (bloquer, interroger ou attendre sa WaitHandle).

4
Stephen Cleary

Vous pouvez utiliser la fonction ContinueWith avec votre routine comme premier argument et un planificateur de tâches comme second argument fourni par TaskScheduler.FromCurrentSynchronizationContext ().

Ça va comme ça:

var task1 = new Task(() => {do_something_in_a_remote_thread();} );

task1.ContinueWith(() =>  {do_something_in_the_ui_thread();},
TaskScheduler.FromCurrentSynchronizationContext());
0