6

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?

9
  • What type are things? Commented Nov 20, 2017 at 20:34
  • 2
    tasks.Add(Task.Factory.StartNew(() => {//Do stuff}); await Task.WhenAll(tasks); That will start said task and add it to the list. Then what all tasks have been added, it will sit on the await line until all have completed. Commented Nov 20, 2017 at 20:53
  • @ZexksMarquise StartNew is dangerous, especially here where his //do stuff contains the await keyword. StartNew does not handle anonymous functions declared async Commented Nov 20, 2017 at 21:41
  • @ScottChamberlain Notice I didn't async DoStuff. Unless he's going to await something inside of DoStuff it's not needed. The await on WhenAll is enough to hold until the list of tasks is finished. Commented Nov 20, 2017 at 21:48
  • @ZexksMarquise Ok, and what about not passing in a TaskScheduler? when you don't specify one it uses TaskScheduler.FromCurrentSynchronizationContext which can make your task from StartNew run on the UI thread instead of a background thread if you are doing this from inside a task already on the background. The article I linked to describes this issue. Commented Nov 20, 2017 at 21:54

2 Answers 2

9

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.

Sign up to request clarification or add additional context in comments.

1 Comment

Understood, thanks! Wasn't entirely sure what the difference was but you cleared it up perfectly.
1

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.