This question already has an answer here:
When I chain a method using ContinueWith and if that method uses async Action, then why is the chained method not waited for completion when we do Task.WhenAll?
For example following code
var str1 = "str1";
var str2 = "str2";
var task0 = Task.Run( async ()=> {
await Task.Delay(1000);
return "changed";
});
var task1 = task0.ContinueWith(async prevTask => {
await Task.Delay(5000);
str1 = prevTask.Result;
});
var task2 = task0.ContinueWith(prevTask => {
Task.Delay(1000).Wait();
str2 = prevTask.Result;
});
await Task.WhenAll(task0, task1, task2);
Console.WriteLine(str1);
Console.WriteLine(str2);
produces output
str1
changed
But when I try to do same with another task with async action but blocking call to Task.Delay, then that method works fine. For example, adding task 3 as
....
var str3 = "str3";
var task3 = task0.ContinueWith(async prevTask => {
Task.Delay(2500).Wait();
str3 = prevTask.Result;
});
await Task.WhenAll(task0, task1, task2, task3);
Console.WriteLine(str1);
Console.WriteLine(str2);
Console.WriteLine(str3);
produces following output
str1
changed
changed
While debugging, I also saw the state of task1 as 'Ran to completion', which I don't understand why. Seems like await inside ContinueWith is causing the task to assume it has completed, but I don't understand why?
Link to functioning code: http://rextester.com/WFRTKY13796
The type of task1
is Task<Task>
so you need to await the tasks to get the resultant set. This is easily done by adding await
to the line while the second example uses a blocking call. Here's the modified rexster code:
//Rextester.Program.Main is the entry point for your code. Don't change it.
//Compiler version 4.0.30319.17929 for Microsoft (R) .NET Framework 4.5
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace Rextester
{
public class Program
{
public static void Main(string[] args)
{
Test().Wait();
}
public static async Task Test(){
var str1 = "str1_beforeChange";
var str2 = "str2_beforeChange";
var str3 = "str3_beforeChange";
var task0 = Task.Run(async ()=>{
await Task.Delay(2500);
return "afterChange";
});
//await the async continuation
var task1 = await task0.ContinueWith(async prevTask => {
await Task.Delay(5000);
str1 = prevTask.Result;
});
var task2 = task0.ContinueWith(prevTask =>{
Task.Delay(2500).Wait();
str2 = prevTask.Result;
});
var task3 = task0.ContinueWith(prevTask =>{
Task.Delay(1000).Wait();
str3 = prevTask.Result;
});
await Task.WhenAll(task0, task1, task2, task3);
Console.WriteLine(str1);
Console.WriteLine(str2);
Console.WriteLine(str3);
}
}
}
ContinueWith
does not supportasync
delegates, thus resulting task represent only first synchronous part of asynchronous delegate. You need to callUnwrap()
, if you want get task which represent completion of asynchronous delegate as whole. - PetSerAlContinueWith
. You're almost always better off usingawait
to schedule continuations to tasks. UsingContinueWith
directly is much harder to do correctly. - Servy