Use an async callback with Task.ContinueWith

匿名 (未验证) 提交于 2019-12-03 02:14:01

问题:

I'm trying to play a bit with C#'s async/await/continuewith. My goal is to have to have 2 tasks which are running in parallel, though which task is executing a sequence of action in order. To do that, I planned to have a List<Task> that represent the 2 (or more) tasks running in parallel, and to use ContinueWith on each of the Task My problem is that the callback in continue with seems not to be executed while the await taskList has already returned.

In order to summarize, here's a sample to illustrate what I'm expecting to happen:

class Program {     static public async Task Test()     {         System.Console.WriteLine("Enter Test");         await Task.Delay(100);         System.Console.WriteLine("Leave Test");     }      static void Main(string[] args)     {         Test().ContinueWith(         async (task) =>         {             System.Console.WriteLine("Enter callback");             await Task.Delay(1000);             System.Console.WriteLine("Leave callback");         },         TaskContinuationOptions.AttachedToParent).Wait();         Console.WriteLine("Done with test");     } } 

The expected output would be

Enter Test Leave Test Enter callback Leave callback Done with test 

However, the output is

Enter Test Leave Test Enter callback Done with test 

Is there a way to make the Task on which ContinueWith is called wait for the provided function to complete before being considered as done? ie. .Wait will wait for both tasks to be completed, the original one, and the one which is returned by ContinueWith

回答1:

When chaining multiple tasks using the ContinueWith method, your return type will be Task<T> whereas T is the return type of the delegate/method passed to ContinueWith.

As the return type of an async delegate is a Task, you will end up with a Task<Task> and end up waiting for the async delegate to return you the Task which is done after the first await.

In order to correct this behaviour, you need to use the returned Task, embedded in your Task<Task>. Use the Unwrap extension method to extract it.



回答2:

When you're doing async programming, you should strive to replace ContinueWith with await, as such:

class Program {   static public async Task Test()   {     System.Console.WriteLine("Enter Test");     await Task.Delay(100);     System.Console.WriteLine("Leave Test");   }    static async Task MainAsync()   {     await Test();     System.Console.WriteLine("Enter callback");     await Task.Delay(1000);     System.Console.WriteLine("Leave callback");   }    static void Main(string[] args)   {     MainAsync().Wait();     Console.WriteLine("Done with test");   } } 

The code using await is much cleaner and easier to maintain.

Also, you should not use parent/child tasks with async tasks (e.g., AttachedToParent). They were not designed to work together.



回答3:

I'd like to add my answer to complement the answer which has already been accepted. Depending on what you're trying to do, often it's possible to avoid the added complexity of async delegates and wrapped tasks. For example, your code could be re-factored like this:

class Program {     static async Task Test1()     {         System.Console.WriteLine("Enter Test");         await Task.Delay(100);         System.Console.WriteLine("Leave Test");     }      static async Task Test2()     {         System.Console.WriteLine("Enter callback");         await Task.Delay(1000);         System.Console.WriteLine("Leave callback");     }      static async Task Test()     {         await Test1(); // could do .ConfigureAwait(false) if continuation context doesn't matter         await Test2();     }      static void Main(string[] args)     {         Test().Wait();         Console.WriteLine("Done with test");     } } 


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!