web-dev-qa-db-fra.com

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

Ainsi, on m'a dit récemment que la façon dont j'utilisais mon fichier .ContinueWith for Tasks 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 de regarder la réponse. Voici un exemple d'utilisation de .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 maintenant qu’il s’agit d’un exemple simple et qu’il fonctionnera très vite, mais supposons simplement que chaque tâche effectue une opération plus longue. Donc, ce que l’on m’a dit, c’est que dans le fichier .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 mes deuxième et troisième tâches ne seraient exécutées 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");
    });
}
88
Travyguy9

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 appelez Wait dans une continuation est d'observer une exception potentielle de l'antécédent dans la continuation elle-même. La même observation se produirait si vous aviez accédé à Result dans le cas d’un Task<T> et aussi si vous aviez accédé manuellement à la propriété Exception . Franchement, je n’appellerais pas Wait ou accès Result car s’il y avait une exception, vous payeriez prix de le relancer ce qui est des frais généraux inutiles. Au lieu de cela, vous pouvez simplement vérifier la propriété IsFaulted de l'antécédent _Task. Vous pouvez également créer des flux de travail définis en chaînant plusieurs chaînes consécutives qui ne sont déclenchées qu'en fonction du succès ou de l'échec avec TaskContinuationOptions.OnlyOnRanToCompletion et TaskContinuationOptions.OnlyOnFaulted.

Maintenant, il n'est pas nécessaire d'observer l'exception de l'antécédent dans la suite, mais vous ne souhaitez peut-être pas que votre flux de travail aille de l'avant si, par exemple, "Étape 1" a échoué. Dans ce cas: spécifier TaskContinuationOptions.NotOnFaulted dans vos appels ContinueWith empêcherait la logique de continuation de se déclencher. 

Gardez à l'esprit que, si vos propres suites n'observent pas l'exception, la personne qui attend la fin de ce flux de travail global sera celle qui l'observera. Soit ils sont Waiting sur le Task en amont, soit ont suivi leur propre continuation pour savoir quand il est terminé. Si c’est le dernier cas, leur continuation devra utiliser la logique d’observation susmentionnée.

116
Drew Marsh

Vous l'utilisez correctement.

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

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

Avoir à appeler prevTask.Wait() dans chaque invocation Task.ContinueWith apparaît comme un moyen étrange de répéter une logique inutile - c’est-à-dire faire quelque chose pour être "ultra-sûr" parce que vous ne comprenez pas réellement ce que fait un certain code. C'est comme vérifier un zéro pour lancer une ArgumentNullException où il aurait été jeté de toute façon.

Donc, non, celui qui vous a dit que c'était faux et ne comprend probablement pas pourquoi Task.ContinueWith existe.

20
Anders Arpi

Qui t'as dit ça?

Citant _ MSDN :

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

Aussi, quel serait le but de Continuer avec s'il n'attendait pas la tâche précédente?

Vous pouvez même le tester 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");
    });
16
ken2k

À partir du MSDN on Task.Continuewith

La tâche renvoyée ne sera pas planifiée pour être exécutée avant le la tâche en cours est terminée. Si les critères spécifiés par le Les options continuationOptions ne sont pas remplies, la tâche de continuation sera être annulé au lieu de prévu.

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

5
mclark1129

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

Stephen Cleary article de blog et l'article de Stephen Toub qu'il référence explique les différences Il y a aussi une discussion dans cette réponse .

1
bizcad

En accédant à Task.Result, vous utilisez une logique similaire à celle de task.wait

0
Sameh

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

Pour plus d'exemples, vous pouvez aller à Chaînage de tâches à l'aide de tâches de continuation , autre lien fourni par Microsoft avec de bons exemples.

0
Amit Dash