.Net在Framework4.0中增加了任务并行库,对开发人员来说利用多核多线程CPU环境变得更加简单,TPL正符合我们本系列的技术需求。因TPL涉及内容较多,且本系列文章为异步程序开发,所以本文并未涉及TPL全部内容。后续会写一个TPL系列的Blog,各位朋友可以关注一下。
TASK
TPL的基础Task,Task是TPL并行编程的最小单元,即表示一个异步操作。利用Task进行异步编程非常简单:
static void Main(string[] args) { BaseTaskDemo(); //BaseTaskDemo2();//两者效果相同 Console.WriteLine(String.Format("Main 线程:{0},获取异步执行结果:{1}", Thread.CurrentThread.ManagedThreadId,task.Result)); Console.ReadLine(); } private static void BaseTaskDemo() { var task = new Task(() => { Thread.Sleep(2000); Console.WriteLine(String.Format("Task 线程:{0}", Thread.CurrentThread.ManagedThreadId)); }); task.Start(); } private static void BaseTaskDemo2() { var task = Task.Run(() => { Thread.Sleep(2000); Console.WriteLine(String.Format("Task 线程:{0}", Thread.CurrentThread.ManagedThreadId)); }); }
程序说明:
1、new Task(Action)创建一个异步任务,参数Action是异步任务要执行的委托对象
2、task.Start()启动异步任务的执行
3、Task.Run(Action)等效于1、2的组合
上面的示例程序实现了异步操作,但主线程无法获知异步任务完成与否。为获取Task的执行结果,上面的程序进行如下修改:
static void Main(string[] args) { var task = TaskForResult(); Console.WriteLine(String.Format("Main 线程:{0}", Thread.CurrentThread.ManagedThreadId)); task.Wait(); if (task.IsCompleted) Console.WriteLine(String.Format("获取异步执行结果:{0}", task.Result)); Console.ReadLine(); } private static Task<int> TaskForResult() { var task = Task.Run(()=> { Thread.Sleep(2000); Console.WriteLine(string.Format("Task 线程:{0},Task执行完成。", Thread.CurrentThread.ManagedThreadId)); return 10; }); return task; }
程序说明:
1、下面代码的原型为Task.Run(Func<int>),Run的参数不再是Action,因为在该任务中我们要返回一个int值,所以应该使用Run(Func<T>)这个重载。
var task = Task.Run(()=> { Thread.Sleep(2000); Console.WriteLine(string.Format("Task 线程:{0},Task执行完成。", Thread.CurrentThread.ManagedThreadId)); return 10; });
2、主线程中调用task.Wait()时,主线程将一直等待异步任务完成或被取消。
3、task.IsCompleted属性用于判断异步任务是否完成
4、task.Result获取异步任务的执行结果(返回值)
上面的示例程序已经实现了主线程获取异步程序的状态及返回值,但如果异步程序非常耗时,则存在主线程需要临时取消耗时异步程序执行的功能。为了满足上述要求,程序可做如下调整:
static void Main(string[] args) { CancellationTokenSource tokenSource = new CancellationTokenSource(5000); var task = TaskForResult2(tokenSource); Console.WriteLine(String.Format("Main 线程:{0}", Thread.CurrentThread.ManagedThreadId)); Console.WriteLine(String.Format("Main 线程:{0},获取异步执行结果:{1}", Thread.CurrentThread.ManagedThreadId, task.Result)); Console.ReadLine(); } private static Task<int> TaskForResult2(CancellationTokenSource tokenSource) { var task = Task.Run(() => { Thread.Sleep(10000); if (!tokenSource.IsCancellationRequested) { Console.WriteLine(String.Format("Task 线程:{0},任务1执行完成。", Thread.CurrentThread.ManagedThreadId)); return 10; } else { return -1; } }, tokenSource.Token); return task; }
程序说明:
1、CancellationTokenSource提供任务取消消息,构造参数 5000 表示CancellationTokenSource在发出5s后超时并取消
2、在Task的委托内部 tokenSource.IsCancellationRequested 获取取消标记
3、task.Result会隐式调用Wait()方法
如果异步Task在执行过程中出现异常,则需要对发生的异常做出响应:
static void Main(string[] args) { CancellationTokenSource tokenSource = new CancellationTokenSource(5000); var task = TaskForResult2(tokenSource); Console.WriteLine(String.Format("Main 线程:{0}", Thread.CurrentThread.ManagedThreadId)); try { //task.Wait(); Console.WriteLine(String.Format("Main 线程:{0},获取异步执行结果:{1}", Thread.CurrentThread.ManagedThreadId, task.Result)); } catch (AggregateException ex) { } Console.ReadLine(); } private static Task<int> TaskForResult2(CancellationTokenSource tokenSource) { var task = Task.Run(() => { Thread.Sleep(1000); if (!tokenSource.IsCancellationRequested) { throw new Exception("抛出异常"); } else { return -1; } }, tokenSource.Token); return task; }
程序说明:
1、在Task中引发的异常需要在 task.Wait()或task.Result时捕获
写在后面:Task的功能远不止上述这些,如Task多任务串行、TaskFactory、Paralle等知识非常有趣和重要。如果你感兴趣的话,可以关注本人后续TPL的文章。