设为首页 加入收藏

TOP

细说.NET中的多线程:使用Task(二)
2015-12-01 14:11:45 来源: 作者: 【 】 浏览:20
Tags:细说 .NET 线程 使用 Task
atp);? // Grandchild


? ? ? ? ? ? ? ? }, atp);


? ? ? ? ? ? });


? ? ? ? ? ? // The following call throws a NullReferenceException (wrapped


? ? ? ? ? ? // in nested AggregateExceptions):


? ? ? ? ? ? parent.Wait();


? ? ? ? }


? ? }


}


取消Task


如果想要支持取消任务,那么在创建Task的时候,需要传入一个CancellationTokenSouce


示例代码:


using System;


using System.Threading;


using System.Threading.Tasks;


namespace MultiThreadTest


{


? ? class Program


? ? {


? ? ? ? static void Main(string[] args)


? ? ? ? {


? ? ? ? ? ? var cancelSource = new CancellationTokenSource();


? ? ? ? ? ? CancellationToken token = cancelSource.Token;


? ? ? ? ? ? Task task = Task.Factory.StartNew(() =>


? ? ? ? ? ? {


? ? ? ? ? ? ? ? // Do some stuff...


? ? ? ? ? ? ? ? token.ThrowIfCancellationRequested();? // Check for cancellation request


? ? ? ? ? ? ? ? // Do some stuff...


? ? ? ? ? ? }, token);


? ? ? ? ? ? cancelSource.Cancel();


? ? ? ? ? ? try


? ? ? ? ? ? {


? ? ? ? ? ? ? ? task.Wait();


? ? ? ? ? ? }


? ? ? ? ? ? catch (AggregateException ex)


? ? ? ? ? ? {


? ? ? ? ? ? ? ? if (ex.InnerException is OperationCanceledException)


? ? ? ? ? ? ? ? ? ? Console.Write("Task canceled!");


? ? ? ? ? ? }


? ? ? ? ? ? Console.ReadLine();


? ? ? ? }


? ? }


}


任务的连续执行


Continuations


任务调度也是常见的需求,Task支持一个任务结束之后执行另一个任务。


Task task1 = Task.Factory.StartNew(() => Console.Write("antecedant.."));


Task task2 = task1.ContinueWith(task =>Console.Write("..continuation"));


Continuations 和Task


Task也有带返回值的重载,示例代码如下:


Task.Factory.StartNew(() => 8)


? ? .ContinueWith(ant => ant.Result * 2)


? ? .ContinueWith(ant => Math.Sqrt(ant.Result))


? ? .ContinueWith(ant => Console.WriteLine(ant.Result));? // output 4


子任务


前面提到了,当你等待一个任务的时候,同时需要等待它的子任务完成。


下面代码演示了带子任务的Task:


using System;


using System.Threading.Tasks;


using System.Threading;


namespace MultiThreadTest


{


? ? class Program


? ? {


? ? ? ? public static void Main(string[] args)


? ? ? ? {


? ? ? ? ? ? Task parentTask = Task.Factory.StartNew(() =>


? ? ? ? ? ? {


? ? ? ? ? ? ? ? int[] results = new int[3];


? ? ? ? ? ? ? ? Task t1 = new Task(() => { Thread.Sleep(3000); results[0] = 0; }, TaskCreationOptions.AttachedToParent);


? ? ? ? ? ? ? ? Task t2 = new Task(() => { Thread.Sleep(3000); results[1] = 1; }, TaskCreationOptions.AttachedToParent);


? ? ? ? ? ? ? ? Task t3 = new Task(() => { Thread.Sleep(3000); results[2] = 2; }, TaskCreationOptions.AttachedToParent);


? ? ? ? ? ? ? ? t1.Start();


? ? ? ? ? ? ? ? t2.Start();


? ? ? ? ? ? ? ? t3.Start();


? ? ? ? ? ? ? ? return results;


? ? ? ? ? ? });


? ? ? ? ? ? Task finalTask = parentTask.ContinueWith(parent =>


? ? ? ? ? ? {


? ? ? ? ? ? ? ? foreach (int result in parent.Result)


? ? ? ? ? ? ? ? {


? ? ? ? ? ? ? ? ? ? Console.WriteLine(result);


? ? ? ? ? ? ? ? }


? ? ? ? ? ? });


? ? ? ? ? ? finalTask.Wait();


? ? ? ? ? ? Console.ReadLine();


? ? ? ? }


? ? }


}


这段代码的输出结果是: 1,2,3


FinalTask会等待所有子Task结束后再执行。


TaskFactory


关于TaskFactory,上面的例子中我们使用了System.Threading.Tasks .Task.Factory属性来快速的创建Task。当然你也可以自己创建TaskFactory,你可以指定自己的TaskCreationOptions,TaskContinuationOptions来使得通过你的Factory创建的Task默认行为不同。


.Net中有一些默认的创建Task的方式,由于TaskFactory创建Task的默认行为不同可能会导致一些不容易发现的问题。


如在.NET 4.5中,Task加入了一个Run的静态方法:


Task.Run(someAction);


如果你用这个方法代替上面例子中的Task.Factory.StartNew,就无法得到正确的结果。原因是Task.Run创建Task的行为默认是默认是拒绝添加子任务的。上面的代码等价于:


? ? Task.Factory.StartNew(someAction, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);


你也可以创建具有自己默认行为的TaskFactory。


无论ThreadPool也好,或者Task,微软都是在想进办法来实现线程的重用,来节省不停的创建销毁线程带来的开销。线程池内部的实现可能在不同版本中有不同的机制。如果可能的话,使用线程池来管理线程仍然是建议的选择。


我们主要介绍了一下Task的基本用法,在我们编程过程中,有一些使用Task来提升程序性能的场景往往是很相似的,微软为了简化编程,在System.Threading.Tasks.Parallel中封装了一系列的并行类,内部也是通过Task来实现

首页 上一页 1 2 3 下一页 尾页 2/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇土耳其屏蔽Reddit 下一篇细说.NET中的多线程:线程池

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: