Basil Kosovan
Basil Kosovan

Reputation: 1069

Async method in Select LINQ operation not executed in parallel as expected

Let's consider this code :

var tasks = actionItems.Select(t => DoSmthAsync());
var resultAll = await Task.WhenAll(tasks);

And method:

public async Task<int> DoSmthAsync()
{
    Debug.WriteLine("Threadid=" + Thread.CurrentThread.ManagedThreadId);
    var result = await DoSmthAnotherAsync();
    Debug.WriteLine("ThreadId=" + Thread.CurrentThread.ManagedThreadId);
    return result;
}

I expect that this will be executed parallel in different threads, but I see that it work in the same thread. Also, I don't understand when a task run?

Upvotes: 2

Views: 1378

Answers (2)

Theodor Zoulias
Theodor Zoulias

Reputation: 44026

I expect that this will be executed parallel in different threads.

This was explained in Victor Wilson's answer, so I'll focus on explaining only this:

Also, I don't understand when a task run?

A Task is usually started when it is created (in which case it is called a hot task). Async methods and all built-in .NET APIs return hot tasks. It is possible to create a non-started Task by using the Task constructor (creating a so called cold task), but these tasks are normally started internally by the same library that created them. Exposing a cold task to the external world, end expecting the callers to Start it, is something that I have never seen, and it would be highly surprising (in an unpleasant way) if existed.

This constructor should only be used in advanced scenarios where it is required that the creation and starting of the task is separated.

var tasks = actionItems.Select(t => DoSmthAsync());

This line of code doesn't create any tasks, so no task is started at this point. The Select LINQ method returns a deferred enumerable sequence, not a materialized collection. The actual materialization of the tasks happens when you call the method Task.WhenAll(tasks).

Upvotes: 2

Victor Wilson
Victor Wilson

Reputation: 1891

You're confusing the async await model with parallel programming.

Select will iterate over your actionItems serially. DoSmthAsync will return control to the caller (the Select iterator in this case) when you await DoSmthAnotherAsync and so you get a list of Task<int> created on one thread.

To run the Select statement in paralell, you'll need to use PLINQ.

var tasks = actionItems.AsParallel().Select(t => DoSmthAsync());

This will produce your anticipated results.

You will then need to await Task.WhenAll(tasks); to wait for the results of the tasks. Then you can access the result of each Task<int> in your tasks list with their respective Result properties.

Upvotes: 3

Related Questions