Rakshith Murukannappa
Rakshith Murukannappa

Reputation: 589

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?

Upvotes: 2

Views: 155

Answers (1)

Fabjan
Fabjan

Reputation: 13676

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

Upvotes: 3

Related Questions