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

88

On m'a donc dit récemment que la façon dont j'utilisais mon .ContinueWith for Tasks n'était pas la bonne façon de les utiliser. Je n'ai pas encore trouvé de preuves de cela sur Internet, alors je vais vous demander les gars et voir quelle est la réponse. Voici un exemple de la façon dont j'utilise .ContinueWith:

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

Maintenant, je sais que c'est un exemple simple et qu'il fonctionnera très rapidement, mais supposons simplement que chaque tâche effectue une opération plus longue. Donc, on m'a dit que dans le .ContinueWith, vous devez dire prevTask.Wait (); sinon, vous pourriez travailler avant la fin de la tâche précédente. Est-ce que c'est possible? J'ai supposé que ma deuxième et troisième tâche ne s'exécuteraient qu'une fois leur tâche précédente terminée.

Ce qu'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");
    });
}
Travyguy9
la source
2
N'utilisez pas StartNew blog.stephencleary.com/2013/08/startnew-is-dangerous.html
Chris Marisic

Réponses:

115

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

La seule raison pour laquelle vous appelleriez Waitune continuation serait d'observer une exception potentielle à l'antécédent dans la continuation elle-même. La même observation se produirait si vous y accédez Resultdans le cas d'un Task<T>et également si vous avez accédé manuellement à la Exceptionpropriété. Franchement, je n'appellerais pas Waitou n'y accéderais pas Resultparce que s'il y a une exception, vous paierez le prix de la relancer, ce qui est des frais généraux inutiles. Au lieu de cela, vous pouvez simplement cocher la IsFaultedpropriété de l'antécédent Task. Vous pouvez également créer des flux de travail fourchus en enchaînant sur plusieurs continuations frères qui ne se déclenchent qu'en cas de succès ou d'échec avec TaskContinuationOptions.OnlyOnRanToCompletionet TaskContinuationOptions.OnlyOnFaulted.

Désormais, il n'est pas nécessaire d'observer l'exception de l'antécédent dans la suite, mais vous ne voudrez peut-être pas que votre flux de travail avance si, par exemple, «l'étape 1» a échoué. Dans ce cas: la spécification TaskContinuationOptions.NotOnFaultedde vos ContinueWithappels empêcherait la logique de continuation de se déclencher même.

Gardez à l'esprit que, si vos propres continuations ne respectent pas l'exception, la personne qui attend que ce flux de travail global se termine sera celle qui l'observera. Soit ils sont Waiten Taskamont, soit ils se sont lancés sur leur propre continuation pour savoir quand elle est terminée. Si c'est ce dernier, leur poursuite devrait utiliser la logique d'observation susmentionnée.

Drew Marsh
la source
2
Enfin, quelqu'un a donné une réponse correcte. @ Travyguy9 Veuillez lire cette réponse @DrewMarsh et en savoir plus surTaskContinuationOptions
Jasper
2
Excellente réponse, je cherchais "Gardez à l'esprit que, si vos propres continuations ne respectent pas l'exception, la personne qui attend que ce flux de travail global se termine sera celle qui l'observera." Une question cependant, quand votre tâche n'est pas attendue, qui est le serveur par défaut? (Impossible de trouver la réponse à cela)
Thibault D.
20

Vous l'utilisez correctement.

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

Source: Méthode Task.ContinueWith (Action en tant que MSDN)

Devoir appeler prevTask.Wait()à chaque Task.ContinueWithinvocation semble être une façon étrange de répéter une logique inutile - c'est-à-dire faire quelque chose pour être "super sûr" parce que vous ne comprenez pas vraiment ce que fait un certain morceau de code. Comme vérifier un null juste pour lancer un ArgumentNullExceptionlà où il aurait été jeté de toute façon.

Donc, non, celui qui vous a dit cela est faux et ne comprend probablement pas pourquoi Task.ContinueWithexiste.

Anders Arpi
la source
16

Qui t'as dit ça?

Citant MSDN :

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

De plus, quel serait le but de Continuer avec s'il 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");
    });
ken2k
la source
5

Depuis le MSDN surTask.Continuewith

La tâche retournée ne sera pas planifiée pour exécution tant que la tâche en cours ne sera pas terminée. Si les critères spécifiés via le paramètre continuationOptions ne sont pas remplis, la tâche de continuation sera annulée au lieu d'être planifiée.

Je pense que la manière dont vous vous attendez à ce que cela fonctionne dans le premier exemple est la bonne manière.

mclark1129
la source
2

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

Le billet de blog de Stephen Cleary et l'article de Stephen Toub auquel il fait référence expliquent les différences. Il y a aussi une discussion dans cette réponse .

bizcad
la source
4
Évalué parce qu'il ne répond pas à la question réelle. Cela ajoute de la valeur, mais devrait être un commentaire.
Sinaesthetic
0

En accédant, Task.Resultvous faites en fait une logique similaire à celletask.wait

Sameh
la source
Oui. Nous pouvons éviter la méthode Wait (). Mais cela ne fonctionne qu'avec les tâches de résultat, par exemple Task <bool>
Alexander Ulmaskulov
Évalué parce qu'il ne répond pas à la question réelle. Cela ajoute de la valeur, mais devrait être un commentaire.
Sinaesthetic
0

Je vais répéter ce que beaucoup ont déjà dit, prevTask.Wait() est inutile .

Pour plus d'exemples, vous pouvez aller à Chaining Tasks using Continuation Tasks , encore un autre lien de Microsoft avec de bons exemples.

Amit Dash
la source