0

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


  • ContinueWith does not support async delegates, thus resulting task represent only first synchronous part of asynchronous delegate. You need to call Unwrap(), if you want get task which represent completion of asynchronous delegate as whole. - PetSerAl
  • There are very few situations where it's appropriate or useful to use ContinueWith. You're almost always better off using await to schedule continuations to tasks. Using ContinueWith directly is much harder to do correctly. - Servy

1 답변


0

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);
        }
    }
}


  • Rather than unwrapping the task after unnecessarily wrapping it in a task, you can just not unnecessarily wrap the task and then you don't have to unwrap it. - Servy

Linked


Related

Latest