await Task.Delay() vs. Task.Delay().Wait()

svenskmand picture svenskmand · Nov 7, 2014 · Viewed 94.1k times · Source

In C# I have the following two simple examples:

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

The first example creates a task that prints "Start", waits 5 seconds prints "Done" and then ends the task. I wait for the task to finish and then print "All done". When I run the test it does as expected.

The second test should have the same behavior, except that the waiting inside the Task should be non-blocking due to the use of async and await. But this test just prints "Start" and then immediately "All done" and "Done" is never printed.

I do not know why I get this behavior :S Any help would be appreciated very much :)

Answer

brz picture brz · Nov 7, 2014

The second test has two nested tasks and you are waiting for the outermost one, to fix this you must use t.Result.Wait(). t.Result gets the inner task.

The second method is roughly equivalent of this:

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

By calling t.Wait() you are waiting for outermost task which returns immediately.


The ultimately 'correct' way to handle this scenario is to forgo using Wait at all and just use await. Wait can cause deadlock issues once you attached a UI to your async code.

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

Even better just use Task.Run to kick off your asynchronous operation:

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