When running a async lambda, use Task.Run(func) or new Func<Task>(func)()?

Viewed 1574

I want to collect/run tasks, and then do Task.WhenAll on them.

var tasks = new List<Task>();
foreach (var thing in things) {
  tasks.Add(Task.Run(async () => {
       // async stuff using thing
  }));
}

var stuffs = await Task.WhenAll(tasks);

Is it correct to use Task.Run here or should I do

tasks.Add(new Func<Task>(async () => {async stuff})());

Or something else entirely?

2 Answers

It usually depends on nature of your async work. If you do something like:

async () => {
   // some heavy CPU work here
   // then some IO
}

It's better to use Task.Run, because otherwise "heavy CPU work" will run on your current thread and you will gain little parallelization.

However, such situation is relatively rare, because for parallelization of heavy CPU work there are other tools. If you have something like this:

async () => {
   // some async IO, like database call or web request
   // some processing
   // some more async IO
}

Then you don't need to Task.Run. Then you can use your second method:

Func<Task> t = async () => {
     // some stuff
};
tasks.Add(t());

If you are doing that on UI thread (in WPF\WinForms) - ensure to use ConfigureAwait(false) inside your async delegate to prevent returning control to UI thread.

Adding to Evk's response, it's also important to be aware of the potential differences in parallelism and if you need to think about thread-safety.

If all of the conditions below are true, then "func" does not need to be thread-safe:

  • You use the new Func approach
  • You don't use ConfigureAwait(false)
  • The SynchronizationContext is a synchronizing context, meaning that it guarantees only one thread can execute code at a time (as in legacy ASP.NET)

Else, you need to think about the thread-safety of "func".

Related