Q:

Spawning tasks within a for loop

I am trying to see how CPU is being utilized when I spawn a lot of tasks using the following code:
static void Main(string[] args)
{
    Stopwatch sw = new Stopwatch();
    sw.Start();
    Console.WriteLine("Main Started");
    MainChild(sw).Wait();
    Console.WriteLine("Main ended: "+sw.Elapsed);
}

static async Task MainChild(Stopwatch sw)
{
    Task[] tasks = new Task[100];
    for (int i = 0; i < 100; i++)
    {
        tasks[i] = Task.Factory.StartNew(new Action(async()=> {
            await Task.Delay(1000);
            Console.WriteLine("Task1 completed: " + sw.Elapsed);
        }));
    }

    await Task.WhenAll(tasks);
}

I noticed that the main thread executes the :"Main Ended: " even before the tasks are executed. Why is this?

A:

The Task.Factory.StartNew method is outdated and almost never used this days, also it doesn't understand async delegates.
Another thing is that the Action delegate returns void and async void is a fire-and-forget type of action. This operation doesn't return Task and is not awaited.
Change Action:
tasks[i] = Task.Factory.StartNew(new Action(async () =>
{
    await Task.Delay(1000);
    Console.WriteLine("Task1 completed: " + sw.Elapsed);
})); 

To Func<Task> and Task.Run:
tasks[i] = Task.Run(new Func<Task>(async () =>
{
    await Task.Delay(1000);
    Console.WriteLine("Task1 completed: " + sw.Elapsed);
}));

As delegate type is inferred by compiler we can shorten it to: 
tasks[i] = Task.Run(async () => ...

Now it is working as expected and waits for all tasks to finish execution

