I'm well aware (thanks to Stephen Toub) that constructing a task with new Task(...)
is generally not recommended and would normally prefer to use Task.Run
, but what is the difference between the three approaches below when passing an async lambda as the task to run? I came across something similar to this in production code, so the code below is a highly contrived and simple example.
When passing an async lambda as a task to Task.Factory.StartNew
and to new Task(...)
(followed by Task.Start
), even though we wait for the returned task the lambda does not finish. However, it does when using Task.Run
- what's the difference here?
(and Stephen Toub states that Task.Run
is exactly equivalent to
Task.Factory.StartNew(SomeTask
CancellationToken.None,
TaskCreationOptions.DenyChildAttach,
TaskScheduler.Default);
See https://devblogs.microsoft.com/pfxteam/task-run-vs-task-factory-startnew/
Here is my code:
using System;
using System.Threading.Tasks;
using System.Threading;
namespace TaskDelay
{
class Program
{
static readonly long t0 = DateTime.Now.Ticks;
private static void Main()
{
Console.WriteLine($"{Time} Starting t1");
var t1 = new Task(async () => await F1(5000, "Task 1"));
t1.Start();
t1.Wait();
Console.WriteLine($"{Time} Starting t2");
var t2 = Task.Factory.StartNew(async () => await F1(5000, "Task 2"),
CancellationToken.None,
TaskCreationOptions.DenyChildAttach,
TaskScheduler.Default);
t2.Wait();
Console.WriteLine($"{Time} Starting t3");
var t3 = Task.Run(async () => await F1(2000, "Task 3"));
t3.Wait();
Console.WriteLine($"{Time} State of {nameof(t1)} is {t1.Status}");
Console.WriteLine($"{Time} State of {nameof(t2)} is {t2.Status}");
Console.WriteLine($"{Time} State of {nameof(t3)} is {t3.Status}");
}
private static async Task F1(int delay, string taskName)
{
await Console.Out.WriteLineAsync($"{Time} Started to run F1 for {taskName}");
await Task.Delay(delay);
await Console.Out.WriteLineAsync($"{Time} Finished running F1 for {taskName}");
}
private static string Time => $"{(int)((DateTime.Now.Ticks - t0) / 10_000),5} ms:";
}
}
And the output is
Notice we never see "Finished running F1 for Task 1" or "Finished running F1 for Task 2".