I have below code, which should wait 10seconds. Problem is that it completes immediately, the WhenAll
method is not working - what am I doing wrong here?
public class WhenAllIsNotWorking
{
public async Task myFunc()
{
await Task.Delay(10000);
}
public async void Init()
{
var tasks = new List<Task>();
for (var i = 0; i < 10; i++)
{
tasks.Add(new Task(async () => { await myFunc(); }));
}
foreach (var task in tasks)
{
task.Start();
}
await Task.WhenAll(tasks);
}
}
Edit, as I didn't mention this originally - above is oversimplified example of my real code - in reality I have a hierarchical tree of entities which I first traverse and register operations per entity (thus why I use new Task()
with combination of task.Start()
). Once I register all the operations, I then group them, and later do task.Start()
on them which allow me to execute operations in ordered way per entity type. Of course that's what I would like to do, if it wasn't for the fact that WhenAll
is not doing it's job here.
My solution, someone closed my question and I can't post answers anymore, anyhow, here is what I ended up doing - thanks for all your help!
public class WhenAllIsNotWorking
{
public async Task myFunc()
{
await Task.Delay(10000);
}
public async Task Init()
{
var tasks = new List<Func<Task>>();
for (var i = 0; i < 10; i++)
{
tasks.Add(async () => { await myFunc(); });
}
var waitList = new List<Task>();
foreach (var task in tasks)
{
waitList.Add(Task.Run(task));
}
await Task.WhenAll(waitList);
}
}
Here is how to do it:
public async Task Init()
{
List<Task<Task>> tasks = new();
for (int i = 0; i < 10; i++)
{
tasks.Add(new Task<Task>(() => myFunc()));
}
// Do some operations in-between, before actually starting the tasks.
foreach (Task<Task> taskTask in tasks)
{
taskTask.Start(TaskScheduler.Default);
}
// Wait for all the tasks to complete
await Task.WhenAll(tasks.Select(t => t.Unwrap()));
}
Key points:
Task
is not async-friendly. It doesn't know what to do with an async
delegate. The delegate end-up being async void
, which is something to avoid.Task<Task>
. This is a nested task the represent the launching of the async operation. It doesn't represent the completion of the async operation. The outer task will complete immediately after the launching of the async operation. The inner task will complete when the async operation completes.Unwrap
method to create a new proxy Task
that represent both the launching and the completion of the async operation.scheduler
argument, whenever you start a Task
with the StartNew
and ContinueWith
APIs. The same recommendation applies to the Start
method. Otherwise your task will be scheduled on the ambient TaskScheduler.Current
, which makes your code dependent on the ambient environment (not a good idea in general).