(DoOnSecond); Task t3 = t1.ContinueWith(DoOnSecond); Task t4 = t2.ContinueWith (DoOnSecond); Console.ReadKey(); } } } 连续任务通过在任务上调用ContinueWith()方法来定义。 t1.ContinueWith(DoOnSecond)方法表示,调用DoOnSecond()方法的新任务应在任务t1结束时立即启动。 t3 = t1.ContinueWith(DoOnSecond); Task t4 = t2.ContinueWith(DoOnSecond); 使用TaskCreationOptions枚举中的值 Task t5 = t1.ContinueWith(DoOnError, TaskContinuationOptions.OnlyOnFaulted
本文记录 dotnet 的一个令人迷惑的设计,在 Task 里,有一个叫 ContinueWith 的方法,此方法可以在 Task 完成时执行传入的委托。 执行某个 Foo 方法之后,再 干自己的活 的代码 var task = Task.Run(Foo).ContinueWith(t => { }); 以上的 ContinueWith 方法里面传入的委托是不管 Task 的执行状态,无论是成功还是失败都能进入。 如果只期望只有在失败时才进入,可以传入 OnlyOnFaulted 参数,代码如下 var task = Task.Run(Foo).ContinueWith(t => 里面的内容全部执行完成,那这个逻辑就诡异了 也就是只有在无需等待 ContinueWith 执行结果的情况下,才可以推荐使用 OnlyOnFaulted 参数。
ContinueWith? 啥东西~~?? 要写可伸缩的软件,一定不能使你的线程阻塞。 ContinueWith便是一个更好的方式,一个任务完成时它可以启动另一个任务。上面的例子不会阻塞任何线程。 当Sum的任务完成时,这个任务会启动另一个任务以显示结果。 ContinueWith会返回对新的Task对象的一个引用,所以为了看到结果,我需要调用一下Wait方法,当然你也可以查询下Result,或者继续ContinueWith,返回的这个对象可以忽略,它仅仅是一个变量 还要指出的是,Task对象内部包含了ContinueWith任务的一个集合。所以,实际上可以用一个Task对象来多次调用ContinueWith。 任务完成时,所有ContinueWith任务都会进入线程池队列中,在构造ContinueWith的时候我们可以看到一个TaskContinuationOptions枚举值,不能忽视,看看它的定义: ?
下面就演示一下如何使用ContinueWith方法。 首先看下ContinueWith方法的原型。 public Task ContinueWith( Action<Task> continuationAction )采用一个Action<Task>类型的委托。 最关键的是ContinueWith的还有一个重载版本可以带一个TaskScheduler对象参数,该对象负责执行被调度的任务。 因此在ContinueWith获取任务执行的结果的并反馈到控件的任务调度上不能使用线程池任务调用器,而要使用同步上下文任务调度器去调度,即采用ui这个线程去调用ContinueWith方法所绑定的回调用函数即 ------ 到目前为止,我平常用到的异步编程模式也就这么多了,当然Task类的ContinueWith还有很多重载的版本,会提供不一样效果。
使用ContinueWith进行异常处理: 使用ContinueWith在异常发生时立即处理。 立即处理异常 这个示例演示了如何使用ContinueWith在异常发生时立即处理: using System; usingSystem.Linq; usingSystem.Threading.Tasks (HandleException, TaskContinuationOptions.OnlyOnFaulted), Task.Run(()=>PerformTask()).ContinueWith (HandleException, TaskContinuationOptions.OnlyOnFaulted), Task.Run(()=>PerformTask()).ContinueWith 使用ContinueWith的即时异常处理: 在第二个示例中,ContinueWith与TaskContinuationOptions.OnlyOnFaulted选项一起使用,以在异常发生后立即处理。
这意味着像 StartNew 或 ContinueWith 创建任务的执行操作将被视为 System.Threading.Tasks.TaskScheduler.Default// 当前计划程序 { Thread.Sleep(1000); Console.WriteLine("child"); }); }); t.ContinueWith }, TaskCreationOptions.AttachedToParent); }, TaskCreationOptions.DenyChildAttach); t.ContinueWith (t => { t.Result.ContinueWith(tt => { (t => { Console.WriteLine($"ContinueWith {Thread.CurrentThread.ManagedThreadId
Task.ContinueWith() 的重载方法非常多,可以参考:https://docs.microsoft.com/zh-cn/dotnet/api/system.threading.tasks.task.continuewith view=netcore-3.1#-- 这里我们使用的构造函数定义如下: public Task ContinueWith(Action<Task> continuationAction); 一个简单的示例 复杂的延续任务 经过上一小节,我们学习了 ContinueWith() 来延续任务,现在我们来学习更多的重载方法,实现更加复杂的延续。 ContinueWith() 重载方法很多,它们的参数都含有下面几种参数之一或多个。 continuationAction 类型:Action 或 Func 一个要执行的任务。 TaskContinuationOptions 可以在以下重载上使用: ContinueWith(Action, CancellationToken, TaskContinuationOptions,
Thread.CurrentThread.ManagedThreadId); } var taskA = Task.Run(() => DateTime.Now); var taskB = taskA.ContinueWith (time => Console.WriteLine(time.Result)); await taskB; 模仿Task,我们给Job也添加ContinueWith方法。 { var job = new Job(work); job.Start(); return job; } public Job ContinueWith return this; } } Job.Run(() => { Thread.Sleep(1000); Console.WriteLine("11"); }).ContinueWith public void GetResult() { } public void OnCompleted(Action continuation) { _job.ContinueWith
Task.ContinueWith 往一个 Task 注册回调,有两种方式:直接调用 Task 实例的 ContinueWith 方法,或者使用 await 关键词。 ContinueWith 的产物:ContinuationTask 调用 ContinueWith 本质上是创建了一个新的 Task(后面简称为 ContinuationTask),而这个 ContinuationTask Task.Run(() => Console.WriteLine(1)) .ContinueWith(t => Console.WriteLine(2)) .ContinueWith(t 其他 public 的 ContinueWith 可以看做是对这些 private 方法的封装。 Task 与 await 与 ContinueWith 相比,await 给我们提供了更加简单的 Task 的使用方式。
// 将用户输入的坐标投影为地图的坐标系坐标 var task1 = geoSvc1.ProjectPointAsync(point1, map.SpatialReference); task1.ContinueWith Result; // 目的地坐标投影为地图坐标 var task2 = geoSvc2.ProjectPointAsync(point2, map.SpatialReference); task2.ContinueWith this.CreateBufferParameters(point2); var buffTask = geoSvc3.BufferTaskAsync(buffParam); // 做一次缓冲查询 buffTask.ContinueWith }; // 求距离 var disTask1 = geoSvc1.DistanceTaskAsync(point1, point2, disParam); disTask1.ContinueWith var disTask2 = geoSvc2.DistanceTaskAsync(point1, buffGeometry.Geometry, disParam); disTask2.ContinueWith
五、异步等待 如果需要在某个Task执行之后接着执行后续的操作,我们可以调用其ContinueWith方法指定待执行的操作,现在我们将这个方法定义Job类型上。 Job与Task的ContinueWith有些差异,在这里我们认为ContinueWith指定的也是一个Job,那么多个Job则可以按照预先编排的顺序构成一个链表。 ContinueWith方法会将指定的Action委托封装成Job并添加到链表末端。 (continuation); } return this; } } 利用ContinueWith方法实现异步操作的按序执行体现在如下的程序中。 ("Baz1"); }); Job.Run(() =>{ Thread.Sleep(100); Console.WriteLine("Foo2"); }).ContinueWith(_
int> task = Task.Run(() => SomeSyncOperation());int result = task.Result; // 阻塞直到任务完成2.2 继续异步操作Task.ContinueWith task.ContinueWith(t =>{ int result = t.Result; // 使用结果});2.3 异常处理在基于Task的异步编程中,异常处理通常通过ContinueWith task.ContinueWith(t =>{ if (t.IsFaulted) { // 处理异常 }});3. processor.Completion.ContinueWith(t =>{ if (t.IsFaulted) { // 处理异常 }});
task任务之CancellationTokenSource的用法; ②task的线程管控方法Task..Wait(time),Task.WaitAll(), Task.WaitAny(),task.ContinueWith 任一任务完成就往下执行 Console.WriteLine(taskFactory.Status); Task taskTest = taskFactory.ContinueWith (DoOnSecond);//ContinueWith用法举例 } static void DoOnSecond(Task t) {
;
Console.ReadLine();
加深对异步的理解
我们都知道 ContinueWith 主要起 延续任务的作用,写起来十分繁琐!. 下面展示使用ContinueWith 和 async/await 的两种方式的代码量:
///
任务 在async和await,当时可以使用continueWith来延迟执行一些方法,但是continueWith并不会捕捉`SynchronizationContext `,所以建议使用await代替 continueWith ❌下面例子就是使用continueWith private void button1_Click(object sender, EventArgs e) { Debug.WriteLine ("RunAsync returned:"+task.Result); // 因为是使用的continueWith,所以线程ID与UI线程并不一致 Debug.WriteLine ("ContinueWith:" + Thread.CurrentThread.ManagedThreadId); }); } public async Task<int> RunAsync() { return await Task.FromResult(1 + 1); } ☑️应该使用await来代替continueWith private async void button1_
如果我们要在CSharpScript.Create()函数创建一个脚本,通过函数ContinueWith 组成一个完整的脚本运行。 正确方式如下: var s0 = CSharpScript.Create("int x = 1;"); var s1 = s0.ContinueWith("int y = 2;") ; var s2 = s1.ContinueWith<int>("x + y"); Console.WriteLine(s2.RunAsync b03f5f7f11d50a3a"); var s0 = CSharpScript.Create("int x = 1;"); var s1 = s0.ContinueWith ("int y = 2;"); var s2 = s1.ContinueWith<int>("x + y"); Console.WriteLine
要使用延续任务,只需要在一个任务上调用ContinueWith方法并传递一个委托,委托的参数代表要延续的任务,可以在委托中使用参数来操作前一个任务。 另外,还可以向ContinueWith方法传递一个TaskContinuationOptions枚举,指定延续任务的执行策略和方式。代码最后调用Wait方法防止在显示结果前退出程序。 开始执行连续的任务:"); Task<int> sumResult = Task.Run(() => SumWithLongTime(500)); Task<int> otherTask = sumResult.ContinueWith (task => task.Result); Task printTask = otherTask.ContinueWith(task => Console.WriteLine($"连续任务的结果是:{ > results[2] = SumWithLongTime(700)); return results; }); parent.Start(); var finished = parent.ContinueWith
3、Task.WaitAny 这个用发同Task.WaitAll,就是等待任何一个任务完成就继续向下执行,将上面的代码WaitAll替换为WaitAny,输出结果如下: 4、Task.ContinueWith 在每次调用ContinueWith方法时,每次会把上次Task的引用传入进来,以便检测上次Task的状态,比如我们可以使用上次Task的Result属性来获取返回值。 ; }) .ContinueWith<bool>(s => { return true; }) .ContinueWith<string>(r = Finished 其实上面的写法简化一下,可以这样写: Task.Factory.StartNew<string>(() => {return "One";}).ContinueWith(ss => { Console.WriteLine(ss.Result);}); 输出One,这个可以看明白了吧~ 更多ContinueWith用法参见:http://technet.microsoft.com/zh-CN
任务 在async和await,当时可以使用continueWith来延迟执行一些方法,但是continueWith并不会捕捉SynchronizationContext,所以建议使用await代替continueWith 下面例子就是使用continueWith private void button1_Click(object sender, EventArgs e) { Debug.WriteLine( ("RunAsync returned:"+task.Result); // 因为是使用的continueWith,所以线程ID与UI线程并不一致 Debug.WriteLine ("ContinueWith:" + Thread.CurrentThread.ManagedThreadId); }); } public async Task<int> RunAsync() (int id, Task task) { return task.ContinueWith( t => print(id), CancellationToken.None
(不要在同步方法里调用异步方法,要异步调用异步,一路异到底) (三)ContinueWith 在现实世界里,经常会发生在一个方法完成之后,在进行下一个方法的调用, 例如,在Button 事件里 (1)异步从网络获取 (2)把源代码写入 C:\File.txt 里 这就需要第二步骤需要在第一步完成之后运行,此时需要用到ContinueWith 方法。 下面的代码简单演示了 ContinueWith (其实,ContinueWith 这个方法的名字就已经很好的解释了他的作用) using System; using System.Threading.Tasks Console.ReadLine(); } static async void Run2Methods(int count) { // 在调动完后,调用 ContinueWith 继续操作 int result = await Task.Run(() => GetSum(count)) .ContinueWith(task => MultiplyNegative1