28

私はC#のasync / await / continuewithで少し遊ぼうとしています。 私の目標は、並行して実行されている2つのタスクを持つ必要があることです。 そうするために、私はList<Task>これは、並列に実行されている2つ(またはそれ以上)のタスクを表します。ContinueWithそれぞれにTask私の問題は、コールバックが継続している間に実行されないようです。await taskListすでに戻ってきました。

要約するために、これが私が何を期待しているかを説明するためのサンプルです:

class Program
{
    static public async Task Test()
    {
        System.Console.WriteLine("Enter Test");
        await Task.Delay(100);
        System.Console.WriteLine("Leave Test");
    }

    static void Main(string[] args)
    {
        Test().ContinueWith(
        async (task) =>
        {
            System.Console.WriteLine("Enter callback");
            await Task.Delay(1000);
            System.Console.WriteLine("Leave callback");
        },
        TaskContinuationOptions.AttachedToParent).Wait();
        Console.WriteLine("Done with test");
    }
}

期待される出力は

Enter Test
Leave Test
Enter callback
Leave callback
Done with test

ただし、出力は

Enter Test
Leave Test
Enter callback
Done with test

タスクを作成する方法はありますかContinueWith完了したと見なされる前に、提供された機能が完了するのを待つというすなわち。元のタスクとContinueWithによって返されるタスクの両方が完了するのを待ちます。


  • を削除してみてくださいawaitからawait Task.Delay(1000); - Alessandro D'Andria
  • 確かにそれはうまくいった。何も待っていなかったので、私は待ち合わせと非同期を取り除きました。ただし、それが機能する理由を見逃しています。理論的な説明はありますか? - chouquette
  • 「コールバックを終了」しますか。 「テストが完了しました」の後に表示されますか。 - Francesco De Lisi
  • 呼び出しtask.ContinueWith(async (task) => { .. })ラップされたタスクの詳細については、実際にTask< Task>を返します。ここに。これはまさにあなたが探しているものですか? - noseratio
  • 不要な場合はラップされたタスクを使用しないでください。こちら私が意味したのは。 - noseratio

3 답변


35

を使用して複数のタスクを連鎖する場合ContinueWithメソッド、あなたの戻り値の型になりますTask<T>一方Tに渡されるデリゲート/メソッドの戻り型ContinueWith

非同期デリゲートの戻り型はTask、あなたはに終わるでしょうTask<Task>そして非同期デリゲートがあなたを返すのを待つことになるTaskこれは最初の後に行われますawait

この動作を修正するには、返されたコードを使用する必要があります。Taskに埋め込まれてTask<Task>。使用Unwrapそれを抽出するための拡張メソッド。


24

やっているときasyncプログラミングは、あなたが交換するように努力する必要がありますContinueWithawait、 など:

class Program
{
  static public async Task Test()
  {
    System.Console.WriteLine("Enter Test");
    await Task.Delay(100);
    System.Console.WriteLine("Leave Test");
  }

  static async Task MainAsync()
  {
    await Test();
    System.Console.WriteLine("Enter callback");
    await Task.Delay(1000);
    System.Console.WriteLine("Leave callback");
  }

  static void Main(string[] args)
  {
    MainAsync().Wait();
    Console.WriteLine("Done with test");
  }
}

使用しているコードawaitはるかにクリーンでメンテナンスが簡単です。

また、親/子タスクを一緒に使用しないでください。asyncタスク(例:AttachedToParent)彼らは一緒に働くようには設計されていません。


5

すでに受け入れられている答えを補完するために私の答えを追加したいと思います。あなたがやろうとしていることによっては、非同期デリゲートやラップされたタスクの複雑さが増すのを避けることがしばしば可能です。例えば、あなたのコードは次のようにリファクタリングすることができます:

class Program
{
    static async Task Test1()
    {
        System.Console.WriteLine("Enter Test");
        await Task.Delay(100);
        System.Console.WriteLine("Leave Test");
    }

    static async Task Test2()
    {
        System.Console.WriteLine("Enter callback");
        await Task.Delay(1000);
        System.Console.WriteLine("Leave callback");
    }

    static async Task Test()
    {
        await Test1(); // could do .ConfigureAwait(false) if continuation context doesn't matter
        await Test2();
    }

    static void Main(string[] args)
    {
        Test().Wait();
        Console.WriteLine("Done with test");
    }
}

リンクされた質問


関連する質問

最近の質問