.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 才会执行。到此,似乎实现了我们的要求。

时间: 2024-10-10 05:27:16

.Net 异步随手记(二)的相关文章

.Net 异步随手记(三)

从<.Net 异步随手记(二)>来看,总感觉还差点儿什么,就是对不同情况的处理.比如当一个 Task 完成了后,我想让它继续执行 T1,如果被取消了就去执行 T2,如果...就去执行 T3,那怎么写呢? 过程是痛苦的,我笨了两天,终于搞通了.之前一直疑惑,为什么引发了 CancellationTokenSource 的 Cancel 方法,捕捉到的 Task 的 IsCanceled 属性依然是 False,今天晚上终于捕捉到了,看来还是自己写的有问题,不废话了,把代码记录一下: 1 name

JQuery日记6.5 Javascript异步模型(二)

mnesia在频繁操作数据的过程可能会报错:** WARNING ** Mnesia is overloaded: {dump_log, write_threshold},可以看出,mnesia应该是过载了.这个警告在mnesia dump操作会发生这个问题,表类型为disc_only_copies .disc_copies都可能会发生. 如何重现这个问题,例子的场景是多个进程同时在不断地mnesia:dirty_write/2 mnesia过载分析 1.抛出警告是在mnesia 增加dump

Javascript异步编程之二回调函数

上一节讲异步原理的时候基本上把回掉函数也捎带讲了一些,这节主要举几个例子来具体化一下.在开始之前,首先要明白一件事,在javascript里函数可以作为参数进行传递,这里涉及到高阶函数的概念,大家可以自行google一下. 传统的同步函数需要返回一个结果的话都是通过return语句实现,例如: function foo() { var a = 3, b = 2; return a+b; } var c = foo(); console.log(c); //5 就是说后面的代码console.lo

异步编程(二)基于事件的异步编程模式 (EAP)

一.引言 在上一个专题中为大家介绍了.NET 1.0中提出来的异步编程模式--APM,虽然APM为我们实现异步编程提供了一定的支持,同时它也存在着一些明显的问题--不支持对异步操作的取消和没有提供对进度报告的功能,对于有界面的应用程序来说,进度报告和取消操作的支持也是必不可少的,既然存在这样的问题,微软当然也应该提供给我们解决问题的方案了,所以微软在.NET 2.0的时候就为我们提供了一个新的异步编程模型,也就是我这个专题中介绍的基于事件的异步编程模型--EAP. 实现了基于事件的异步模式的类将

性能优化之——.NET(C#)调用webService获取客户端IP地址所属区域(异步回调)(二)

朋友们这次分享的是异步回调不是异步调用哦! 请注意喽! 功能描述,接口地址,方法名称以及参数说明,同上篇:.NET(C#)调用webService获取客户端IP地址所属区域(非异步)(一)(LZ比较懒,不想写太多哦!(⊙0⊙)) 实现代码如下: 1 namespace main 2 { 3 class Program 4 { 5 public static string Result = string.Empty; 6 7 static void Main(string[] args) 8 {

浅谈android中异步加载之&quot;取消异步加载&quot;二

首先,我得解释一下为什么我的标题取消异步加载打引号,这是因为可能最后实现效果并不是你自己想象中的那样.大家看取消异步加载,这不是很简单吗?AsyncTask中不是有一个cancel方法吗?直接调用该方法不就行了吗?但是事实上是这样的吗?如果真是这样,我相信我就没有以写这个作为一篇博客的必要了.为什么会有这样的想法呢?实际上源于我上一篇中Demo中的一个BUG,然后解决该BUG,需要去取消异步任务,是怎么样,我们不妨来看看. 首先,还是来一起回顾一下上篇博客中加载进度条Demo吧. AsyncTa

JavaScript异步编程(二) 异步的脚本加载

异步的脚本加载 问题: <head>标签里的大脚本会滞压所有页面渲染工作,使页面在脚本加载完毕前一直处于白屏: <body>标签末尾的大脚本使用户只能看到静态页面,原本应进行渲染的地方却是空的: 解决方案: 对脚本分而治之; 负责让页面更好看.更好用的脚本立即加载,可稍后再加载的脚本稍后再加载. HTML5的async/defer属性 ?<script标签> 经典型和非阻塞型 理想情况下,脚本的加载应该与文档的加载同时进行,并且不影响DOM的渲染. 这样,一旦文档就绪就

JavaScript异步编程(二) 分布式事件

分布式事件 发布/订阅模式分发事件 ?PubSub模式 浏览器允许向DOM元素附加事件处理器: Node的EventEmitter对象 emitter.on('evacuate', function() {-}); emitter.emit('evacuate'); emit意为触发,负责调用给定事件类型的所有处理器 创建自己的PubSub PubSub.on = function(eventType, handler) { if(!(eventType in this.handlers)) {

.Net 异步随手记(一)

今天要记录的内容摘要是: 什么时候异步代码能“等”在那里,什么时候不会“等” 这两天Coding的时候碰到一个事儿,就是想让异步等在那里结果却直接执行过去了,比如这样: 1 async static void Count() 2 { 3 Console.WriteLine("Task Void 开始"); 4 Console.WriteLine(Thread.CurrentThread.ManagedThreadId); 5 int count = 0; 6 while (count