Search code examples
c#async-awaittap

Why Task stops executing on call any async method if I create it with new Task(action) -> .Start() instead of Task.Run()?


I found out that if construct a new task using the following way: var task = new Task(async action); task.Start();, the any call of any async method (await Task.Delay(), await new WebClient().DownloadStringAsync(), etc.) supposingly ignores await operator and immediately backs to the main thread.

If I run the same action using Task.Run(async action) way, then the same code works as expected.

Sample code:

class Program
{
    static async Task Main()
    {
        // way1 - it's working
        var goodTask = Task.Run(async () => await ProblemMethod("goodTask"));
        await goodTask;
        Console.WriteLine($"{DateTime.Now:HH:mm:ss} :: {nameof(goodTask)} is finished.");

        // way2 - it fails without any exceptions on line with GetRequestStreamAsync
        var poorTask = new Task(async () => await ProblemMethod("poorTask"));
        poorTask.Start();
        await poorTask;
        Console.WriteLine($"{DateTime.Now:HH:mm:ss} :: {nameof(poorTask)} is finished.");

        Console.ReadLine();
    }

    static async Task ProblemMethod(string taskName)
    {
        Console.WriteLine($"{taskName} :: {DateTime.Now:HH:mm:ss} :: It's a first line.");
        await Task.Delay(2000);
        Console.WriteLine($"{taskName} :: {DateTime.Now:HH:mm:ss} :: It's a last line.");
    }
}

Console output:

goodTask :: 18:52:50 :: It's a first line.
goodTask :: 18:52:52 :: It's a last line.
18:52:52 :: goodTask is finished.
poorTask :: 18:52:52 :: It's a first line.
18:52:52 :: poorTask is finished.
poorTask :: 18:52:54 :: It's a last line.

Why so strange behaviour? And, yeah, I know that the preferred and recommended way is to run tasks immediately, via Task.Run or Task.Factory.StartNew, but what should I do if I don't want to run my tasks immediately?


Solution

  • The answer is discussed here. Essentially the Task construction takes an Action<> and not a Func<> and so the async delegate is not really being treated as such.