web-dev-qa-db-ja.com

Task.Delay()対Task.Delay()。Wait()を待つ

C#には、次の2つの簡単な例があります。

[Test]
public void TestWait()
{
    var t = Task.Factory.StartNew(() =>
    {
        Console.WriteLine("Start");
        Task.Delay(5000).Wait();
        Console.WriteLine("Done");
    });
    t.Wait();
    Console.WriteLine("All done");
}

[Test]
public void TestAwait()
{
    var t = Task.Factory.StartNew(async () =>
    {
        Console.WriteLine("Start");
        await Task.Delay(5000);
        Console.WriteLine("Done");
    });
    t.Wait();
    Console.WriteLine("All done");
}

最初の例では、「開始」を出力するタスクを作成し、5秒間待って「完了」を出力してからタスクを終了します。タスクが完了するのを待ってから、「すべて完了」を印刷します。テストを実行すると、期待どおりに動作します。

2番目のテストの動作は同じである必要がありますが、asyncとawaitを使用しているため、タスク内での待機は非ブロッキングでなければなりません。ただし、このテストでは「開始」と印刷され、すぐに「すべて完了」と「完了」は印刷されません。

私はなぜこの振る舞いをするのか分かりません:Sどんな助けも大歓迎です:)

45
svenskmand

2番目のテストには2つのネストされたタスクがあり、最も外側のタスクを待っています。これを修正するには、t.Result.Wait()を使用する必要があります。 t.Resultは内部タスクを取得します。

2番目の方法は、これとほぼ同等です。

public void TestAwait()
{
  var t = Task.Factory.StartNew(() =>
            {
                Console.WriteLine("Start");
                return Task.Factory.StartNew(() =>
                {
                    Task.Delay(5000).Wait(); Console.WriteLine("Done");
                });
            });
            t.Wait();
            Console.WriteLine("All done");
}

t.Wait()を呼び出すことで、すぐに戻る最も外側のタスクを待っています。


このシナリオを処理するための最終的に「正しい」方法は、Waitをまったく使用せず、awaitを使用することです。 Waitは、非同期コードにUIを接続すると デッドロックの問題 を引き起こす可能性があります。

    [Test]
    public async Task TestCorrect() //note the return type of Task. This is required to get the async test 'waitable' by the framework
    {
        await Task.Factory.StartNew(async () =>
        {
            Console.WriteLine("Start");
            await Task.Delay(5000);
            Console.WriteLine("Done");
        }).Unwrap(); //Note the call to Unwrap. This automatically attempts to find the most Inner `Task` in the return type.
        Console.WriteLine("All done");
    }

さらに良いのは、Task.Runを使用して非同期操作を開始することです。

    [TestMethod]
    public async Task TestCorrect()
    {
        await Task.Run(async () => //Task.Run automatically unwraps nested Task types!
        {
            Console.WriteLine("Start");
            await Task.Delay(5000);
            Console.WriteLine("Done");
        });
        Console.WriteLine("All done");
    }
43
brz