prevTask.Wait()は、(タスクライブラリの)ContinueWithで使用することをお勧めしますか?


88

それで、最近、私は.ContinueWith for Tasksをどのように使用しているかが、適切な使用方法ではないと言われました。私はインターネットでこれの証拠をまだ見つけていないので、皆さんに尋ねて、答えが何であるかを確認します。これが.ContinueWithの使用例です。

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

これは簡単な例であり、非常に高速に実行されることを知っていますが、各タスクがより長い操作を実行すると仮定します。だから、私が言われたことは、.ContinueWithでは、prevTask.Wait();と言う必要があるということです。それ以外の場合は、前のタスクが完了する前に作業を行うことができます。それは可能ですか?2つ目と3つ目のタスクは、前のタスクが終了して初めて実行されると想定しました。

コードの書き方を教えられた:

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");
    });
}

回答:


115

Ehhh ....現在の回答のいくつかは何かが欠けていると思います:例外で何が起こりますか?

Wait継続で呼び出す唯一の理由は、継続自体の前件からの潜在的な例外を観察することです。aの場合にアクセスResultした場合、Task<T>およびExceptionプロパティに手動でアクセスした場合にも、同じことが起こります。率直に言って、私が電話しWaitたりアクセスしたりするResultことはありません。例外がある場合、再調達の代金を払うからです。これは不必要なオーバーヘッドです。代わりにIsFaulted、前件からプロパティを確認するだけですTask。または、TaskContinuationOptions.OnlyOnRanToCompletionとで成功または失敗のどちらかに基づいてのみ起動する複数の兄弟継続をチェーンすることにより、分岐したワークフローを作成できますTaskContinuationOptions.OnlyOnFaulted

これで、継続の前件の例外を監視する必要はありませんが、たとえば「ステップ1」が失敗した場合にワークフローを進めたくない場合があります。その場合:呼び出しに指定するTaskContinuationOptions.NotOnFaultedContinueWith、継続ロジックが実行されることさえありません。

あなた自身の継続が例外を観察しない場合、この全体的なワークフローが完了するのを待っている人がそれを観察することになることに注意してください。どちらか彼らはしているWait上INGのTask上流又はそれが完了したときに知っている自分自身の継続に留めています。後者の場合、彼らの継続は前述の観察ロジックを使用する必要があります。


2
とうとう誰かが正解しました。@ Travyguy9この@DrewMarshの回答を読んで、詳細についてお読みくださいTaskContinuationOptions
Jasper

2
すばらしい答えでした。「もしあなた自身の継続が例外を観察しなければ、このワークフロー全体が完了するのを待っている人がそれを観察する人になることに注意してください。」1つの質問ですが、タスクが待機されていない場合、デフォルトの待機者は誰ですか?(これに対する答えを見つけることができませんでした)
Thibault D.

20

あなたはそれを正しく使用しています。

ターゲットタスクが完了したときに非同期的に実行される継続を作成します

ソース:Task.ContinueWithメソッド(Action as MSDN)

呼び出すために持ってprevTask.Wait()おきにTask.ContinueWith起動すると、不要なロジックを繰り返す奇妙な方法のように思える-つまり、あなたが実際にコードの特定のビットが何をするのか理解していないので、「わから大型のスーパー」に何かをやって。ArgumentNullExceptionとにかくスローされたはずの場所にスローするためにnullをチェックするようなものです。

だから、いいえ、あなたが間違っていると言った人は誰でも、なぜTask.ContinueWith存在するのか理解していないでしょう。


16

誰があなたにそれを言ったの?

MSDNの引用:

ターゲットタスクが完了したときに非同期的に実行される継続を作成します。

また、前のタスクが完了するのを待たなかった場合、「続行」の目的は何ですか?

自分でテストすることもできます。

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");
    });

5

以下からのMSDNTask.Continuewith

返されたタスクは、現在のタスクが完了するまで実行がスケジュールされません。continuationOptionsパラメーターで指定された基準が満たされない場合、継続タスクはスケジュールされずにキャンセルされます。

最初の例で期待通りに機能するのは正しい方法だと思います。


2

Task.Factory.StartNewの代わりにTask.Runの使用を検討することもできます。

Stephen Clearyのブログ投稿彼が参照するStephen Toubの投稿は、違いを説明しています。この回答には議論もあります


4
実際の質問に対応していないため、反対票が投じられました。それはいくつかの価値を追加しますが、コメントであるべきです。
2017年

0

アクセスTask.Resultすることで、実際に同様のロジックを実行していますtask.wait


はい。Wait()メソッドは回避できます。しかし、それは結果タスクでのみ機能します。例:Task <bool>
Alexander Ulmaskulov 2017

実際の質問に対応していないため、反対票が投じられました。それはいくつかの価値を追加しますが、コメントであるべきです。
2017年

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.