.Net 异步随手记(二)

Task.ContinueWith

这货,和 await 一样有“陷阱”。^^,因为写 ContinueWith 不能直观如人的“过程性”思维,写在 ContinueWith 括号里的部分不一定只在发起 ContinueWith 的任务完成后完成,比如这样:

 1 namespace TaskConsole
 2 {
 3     class Program
 4     {
 5         static void Main(string[] args)
 6         {
 7             while (true)
 8             {
 9                 var enter = Console.ReadLine();
10 
11                 if (enter.ToLower() == "continue") { Entry(); }
12                 else { break; }
13             }
14         }
15 
16 
17         async static void DoWork(string workerName)
18         {
19             await Task.Delay(1000);
20             Console.WriteLine("{0} is working @ Thread Id {1}", workerName, System.Threading.Thread.CurrentThread.ManagedThreadId);
21         }
22 
23         async static void DoClean(string cleaner)
24         {
25             await Task.Delay(1000);
26             Console.WriteLine("{0} is cleaning @ Task Id {1}", cleaner, Task.CurrentId);
27         }
28 
29         async static Task Entry()
30         {
31             var task = Task.Factory.StartNew(() => DoWork("工作者1")).
32                         ContinueWith(backer => DoClean("清理者1")).
33                         ContinueWith(backer => DoClean("清理者2")).
34                         ContinueWith(backer => DoClean("清理者3"));
35         }
36     }
37 }

运行一下试试?如果过程中没有 await Task.Delay 参与,看起来基本是按顺序的,但是这句一加上就立马露陷了,看

那如何让 ContinueWith 按我们的心思走呢?对了,还有其它参数可以用,让我们随便试试

1 var task = Task.Factory.StartNew(() => DoWork("工作者1")).
2                         ContinueWith(backer => DoClean("清理者1"), TaskContinuationOptions.NotOnRanToCompletion).
3                         ContinueWith(backer => DoClean("清理者2"), TaskContinuationOptions.OnlyOnRanToCompletion).
4                         ContinueWith(backer => DoClean("清理者3"), TaskContinuationOptions.OnlyOnRanToCompletion);

运行一下,咦,发生了什么?

为什么清理者都不工作了?问题出在参数 【TaskContinuationOptions.NotOnRanToCompletion】,它起了作用,它告诉第一个清理者“当工作者任务不是正常完成时你才能启动”,所以清理者1就没有机会启动,它没有机会启动,后面的清理者2、清理者3自然都没有办法启动。到这里只知道 ContinueWith 还有参数可以利用,但是没有达到我们的目的,继续!先把两个 void 方法改造一下

 1 async static Task DoWork(string workerName)
 2 {
 3     await Task.Delay(1000);
 4     Console.WriteLine("{0} is working @ Thread Id {1}", workerName, System.Threading.Thread.CurrentThread.ManagedThreadId);
 5 }
 6 
 7 async static Task DoClean(string cleaner)
 8 {
 9     await Task.Delay(1000);
10     Console.WriteLine("{0} is cleaning @ Task Id {1}", cleaner, Task.CurrentId);
11 }

变成了 Task 的方法,就有了控制的前提条件,接下来改造一下 ContinueWith 的流程

1 {
2     var taskWorker = Task.Factory.StartNew(() => DoWork("工作者1"));
3     await taskWorker.Result;
4     var c1 = await taskWorker.ContinueWith(backer => DoClean("清理者1"), TaskContinuationOptions.OnlyOnRanToCompletion);
5     var c2 = await c1.ContinueWith(backer => DoClean("清理者2"), TaskContinuationOptions.OnlyOnRanToCompletion);
6     var c3 = await c2.ContinueWith(backer => DoClean("清理者3"), TaskContinuationOptions.OnlyOnRanToCompletion);
7 }

把原来写在一起的一堆 ContinueWith 分开,让它们各自“拥有身份”,并且按顺序去继承上一个 Task,只有当指定的 Task 执行完成到 RanToCompletion 的状态时自身才被启用,并且 await 的使用保证了 taskWorker 运行完成 C1 才会执行,C1 运行完 C2 才会执行,C2 运行完 C3 才会执行。到此,似乎实现了我们的要求。

原文地址:https://www.cnblogs.com/cinlap/p/5762792.html