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".
