Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Task.WhenAll - not waiting for child tasks to finish

I have some problems regarding the TPL. As you can see, I am creating 2 simple tasks and adding them to a list. Problem (expected behavior) is that tasks return immediately after hitting the "await" in WorkMethodAsync therefore rendering Task.WhenAll useless. Is there a way to wait for child tasks to finish? The only workaround I've found is faking WorkMethodAsync to be synchronous.

private async void button1_Click(object sender, EventArgs e)
{
    Tasks = new List<Task>();
    var myTask1 = Task.Factory.StartNew(async () => await WorkMethodAsync(), CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default);
    var myTask2 = Task.Factory.StartNew(async () => await WorkMethodAsync(), CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default);

    Tasks.Add(myTask1);
    Tasks.Add(myTask2);

    await Task.WhenAll(Tasks.ToArray());
}
private async Task WorkMethodAsync()
{
    while (true)
    {
        await Task.Delay(10000);
    }
    return;
}
like image 438
matelras Avatar asked Oct 29 '15 23:10

matelras


1 Answers

The problem is that you're using Task.Factory.StartNew instead of Task.Run.

StartNew was built before async-await and so doesn't support it very well. In this case since your delegate returns a task and StartNew creates a task the return value is Task<Task>>. You can use Unwrap to get a task that represents the entire async operation:

Task<Task> myTask1 = Task.Factory.StartNew(async () => await WorkMethodAsync(), CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default);
Task<Task> myTask2 = Task.Factory.StartNew(async () => await WorkMethodAsync(), CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default);

Tasks.Add(myTask1.Unwrap());
Tasks.Add(myTask2.Unwrap());

However, there's really no reason to use Task.Factory.StartNew to begin with since TaskCreationOptions.LongRunning doesn't make sense for an async method (more on that on my blog: LongRunning Is Useless For Task.Run With async-await) so you can just use Task.Run:

var myTask1 = Task.Run(() => WorkMethodAsync());
var myTask2 = Task.Run(() => WorkMethodAsync());
like image 79
i3arnon Avatar answered Nov 03 '22 00:11

i3arnon