Task.Factory.StartNew with async lambda and Task.WaitAll
Solution 1
Task.Factory.StartNew
doesn't recognise async
delegates as there is no overload that accepts a function returning a Task
.
This plus other reasons (see StartNew is dangerous) is why you should be using Task.Run
here:
tasks.Add(Task.Run(async () => ...
Solution 2
This doesn't wait because of the async lambda. So how am I supposed to await I/O operations in my lambda?
The reason Task.WaitAll
doesn't wait for the completion of the IO work presented by your async lambda is because Task.Factory.StartNew
actually returns a Task<Task>
. Since your list is a List<Task>
(and Task<T>
derives from Task
), you wait on the outer task started by StartNew
, while ignoring the inner one created by the async lambda. This is why they say Task.Factory.StartNew
is dangerous with respect to async.
How could you fix this? You could explicitly call Task<Task>.Unwrap()
in order to get the inner task:
List<Task> tasks = new List<Task>();
tasks.Add(Task.Factory.StartNew(async () =>
{
using (dbContext = new DatabaseContext())
{
var records = await dbContext.Where(r => r.Id = 100).ToListAsync();
//do long cpu process here...
}
}).Unwrap());
Or like others said, you could call Task.Run
instead:
tasks.Add(Task.Run(async () => /* lambda */);
Also, since you want to be doing things right, you'll want to use Task.WhenAll
, why is asynchronously waitable, instead of Task.WaitAll
which synchronously blocks:
await Task.WhenAll(tasks);
Jacob Roberts
Challenges of solving issues is what drives me. I spend most of my days at work solving complex problems and building software solutions. My nights are filled with fun and excitement enjoying the fruits of my labors with the realization that what I have was built on dedication alone.
Updated on May 06, 2020Comments
-
Jacob Roberts almost 4 years
I'm trying to use
Task.WaitAll
on a list of tasks. The thing is the tasks are an async lambda which breaksTasks.WaitAll
as it never waits.Here is an example code block:
List<Task> tasks = new List<Task>(); tasks.Add(Task.Factory.StartNew(async () => { using (dbContext = new DatabaseContext()) { var records = await dbContext.Where(r => r.Id = 100).ToListAsync(); //do long cpu process here... } } Task.WaitAll(tasks); //do more stuff here
This doesn't wait because of the async lambda. So how am I supposed to await I/O operations in my lambda?