0

この質問にはすでに答えがあります。

ContinueWithを使用してメソッドをチェインしていて、そのメソッドが非同期アクションを使用している場合、Task.WhenAllを実行したときに連鎖メソッドが完了を待たないのはなぜですか?

例えば以下のコード

var str1 = "str1";
var str2 = "str2";
var task0 = Task.Run( async ()=> { 
                await Task.Delay(1000);
                return "changed";
            });
var task1 = task0.ContinueWith(async prevTask => {
                await Task.Delay(5000);
                str1 = prevTask.Result;
            });
var task2 = task0.ContinueWith(prevTask => {
                Task.Delay(1000).Wait();
                str2 = prevTask.Result;
            });

await Task.WhenAll(task0, task1, task2);
Console.WriteLine(str1);
Console.WriteLine(str2);

出力を生成する

str1
changed

しかし、非同期アクションを使用してTask.Delayへの呼び出しをブロックしている別のタスクで同じことを試みると、そのメソッドは正常に機能します。たとえば、タスク3を次のように追加します。

....
var str3 = "str3";
var task3 = task0.ContinueWith(async prevTask => {
                Task.Delay(2500).Wait();
                str3 = prevTask.Result;
            });

await Task.WhenAll(task0, task1, task2, task3);
Console.WriteLine(str1);
Console.WriteLine(str2);
Console.WriteLine(str3);

次のような出力を生成します

str1
changed
changed 

デバッグ中に、task1の状態も '完了まで実行'として表示されていましたが、これは理解できません。 ContinueWithの中でタスクが完了したと見なしていますが、その理由はわかりませんか。

機能しているコードへのリンク:http://rextester.com/WFRTKY13796


  • ContinueWithサポートしていませんasyncしたがって、結果のタスクは非同期デリゲートの最初の同期部分のみを表します。電話する必要がありますUnwrap()あなたが全体として非同期のデリゲートの完了を表すタスクを取得したい場合。 - PetSerAl
  • 使用するのが適切または便利な状況はほとんどありませんContinueWith。ほとんどの場合、使用したほうがよいでしょうawaitタスクの継続をスケジュールする。を使うContinueWith直接ですずっと正しくやるのは難しいです。 - Servy

1 답변


0

の種類task1ですTask<Task>そのため、結果セットを取得するためにタスクを待つ必要があります。これは簡単に追加することによって行われますawait2番目の例ではブロッキングコールを使用しています。これが修正されたrexsterコードです。

//Rextester.Program.Main is the entry point for your code. Don't change it.
//Compiler version 4.0.30319.17929 for Microsoft (R) .NET Framework 4.5

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace Rextester
{
    public class Program
    {
        public static void Main(string[] args)
        {
            Test().Wait();                        
        }

        public static async Task Test(){

            var str1 = "str1_beforeChange";
            var str2 = "str2_beforeChange";
            var str3 = "str3_beforeChange";

            var task0 = Task.Run(async ()=>{
                await Task.Delay(2500);
                return "afterChange";
            });

            //await the async continuation
            var task1 = await task0.ContinueWith(async prevTask => {
                await Task.Delay(5000);
                str1 = prevTask.Result;
            });

            var task2 = task0.ContinueWith(prevTask =>{
                Task.Delay(2500).Wait();
                str2 = prevTask.Result;
            });

            var task3 = task0.ContinueWith(prevTask =>{
                Task.Delay(1000).Wait();
                str3 = prevTask.Result;
            });

            await Task.WhenAll(task0, task1, task2, task3);

            Console.WriteLine(str1);
            Console.WriteLine(str2);
            Console.WriteLine(str3);
        }
    }
}


  • 不必要にタスクにラップした後にそのタスクをラップ解除するのではなく、不必要にタスクをラップしないそれからアンラップする必要はありません。 - Servy

リンクされた質問


関連する質問

最近の質問