88 votes

Est-il recommandé d'utiliser prevTask.Wait() avec ContinueWith (de la bibliothèque Tasks) ?

On m'a dit récemment que la façon dont j'utilisais mes .ContinueWith pour les tâches n'était pas la bonne façon de les utiliser. Je n'ai pas encore trouvé de preuve de cela sur Internet, alors je vais vous demander votre avis et voir quelle est la réponse. Voici un exemple de la façon dont j'utilise les .ContinueWith :

public Task DoSomething()
{
    return Task.Factory.StartNew(() =>
    {
        Console.WriteLine("Step 1");
    })
    .ContinueWith((prevTask) =>
    {
        Console.WriteLine("Step 2");
    })
    .ContinueWith((prevTask) =>
    {
        Console.WriteLine("Step 3");
    });
}

Je sais qu'il s'agit d'un exemple simple et qu'il va s'exécuter très rapidement, mais supposez que chaque tâche effectue une opération plus longue. Ce qu'on m'a dit, c'est que dans le .ContinueWith, il faut dire prevTask.Wait() ; sinon, on pourrait effectuer un travail avant que la tâche précédente ne se termine. Est-ce vraiment possible ? J'ai supposé que mes deuxième et troisième tâches ne s'exécuteraient qu'une fois la tâche précédente terminée.

On m'a dit comment écrire le code :

public Task DoSomething()
{
    return Task.Factory.StartNew(() =>
    {
        Console.WriteLine("Step 1");
    })
    .ContinueWith((prevTask) =>
    {
        prevTask.Wait();
        Console.WriteLine("Step 2");
    })
    .ContinueWith((prevTask) =>
    {
        prevTask.Wait();
        Console.WriteLine("Step 3");
    });
}

116voto

Drew Marsh Points 22002

Ehhh.... Je pense que certaines des réponses actuelles manquent quelque chose : que se passe-t-il avec les exceptions ?

La seule raison pour laquelle vous appelleriez Wait dans une continuation serait d'observer une exception potentielle de l'antécédent dans la continuation elle-même. La même observation se produirait si vous accédiez à Result dans le cas d'un Task<T> et aussi si vous avez accédé manuellement au Exception propriété. Franchement, je n'appellerais pas Wait ou accès Result car s'il y a une exception, vous paierez le prix de la relance, ce qui représente des frais généraux inutiles. Au lieu de cela, vous pouvez simplement vérifier le IsFaulted propriété de l'antécédent Task . Vous pouvez également créer des flux de travail bifurqués en enchaînant plusieurs continuations sœurs qui ne s'activent qu'en cas de succès ou d'échec grâce à la fonction TaskContinuationOptions.OnlyOnRanToCompletion y TaskContinuationOptions.OnlyOnFaulted .

Il n'est pas nécessaire de respecter l'exception de l'antécédent dans la suite, mais vous ne voulez peut-être pas que votre flux de travail avance si, par exemple, l'étape 1 échoue. Dans ce cas : spécifier TaskContinuationOptions.NotOnFaulted à votre ContinueWith empêcherait la logique de continuation de se déclencher.

Gardez à l'esprit que, si vos propres continuations ne respectent pas l'exception, c'est la personne qui attend la fin de ce flux de travail global qui va la constater. Soit elle Wait sur le Task en amont ou ont ajouté leur propre continuation pour savoir quand elle est terminée. Dans ce dernier cas, leur suite devra utiliser la logique d'observation susmentionnée.

20voto

Anders Holmström Points 4422

Vous l'utilisez correctement.

Crée une continuation qui s'exécute de manière asynchrone. w La tâche est terminée.

Source : Méthode Task.ContinueWith (Action comme MSDN)

Devoir appeler prevTask.Wait() dans chaque Task.ContinueWith L'invocation semble être un moyen bizarre de répéter une logique inutile - c'est-à-dire de faire quelque chose pour être "super super sûr" parce que vous ne comprenez pas vraiment ce que fait un certain bout de code. Comme vérifier la présence d'un null juste pour lancer un ArgumentNullException où il aurait été jeté de toute façon.

Donc, non, celui qui t'a dit ça a tort et ne comprend probablement pas pourquoi. Task.ContinueWith existe.

16voto

ken2k Points 24640

Qui t'a dit ça ?

Citation : MSDN :

Crée une continuation qui s'exécute de manière asynchrone lorsque la tâche cible cible est terminée.

Aussi, quel serait le but de Continuer Avec si elle n'attendait pas que la tâche précédente soit terminée ?

Vous pouvez même le tester par vous-même :

Task.Factory.StartNew(() =>
    {
        Console.WriteLine("Step 1");
        Thread.Sleep(2000);
    })
    .ContinueWith((prevTask) =>
    {
        Console.WriteLine("I waited step 1 to be completed!");
    })
    .ContinueWith((prevTask) =>
    {
        Console.WriteLine("Step 3");
    });

5voto

Mike C Points 3442

De la MSDN sur Task.Continuewith

La tâche renvoyée ne sera pas programmée pour être exécutée avant que la fonction tâche actuelle soit terminée. Si les critères spécifiés par l'intermédiaire de l'option continuationOptions ne sont pas remplis, la co sera annulée au lieu d'être planifiée.

Je pense que la façon dont vous vous attendez à ce que cela fonctionne dans le premier exemple est la bonne.

2voto

bizcad Points 11

Vous pouvez également envisager d'utiliser Task.Run au lieu de Task.Factory.StartNew.

Stephen Cleary article de blog et les Stephen Toub's qu'il mentionne expliquer les différences. Il y a également une discussion dans cette réponse .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X