查看: 655|回复: 0

[ASP.NET教程] C# 异步编程3 TPL Task 异步程序开发

发表于 2017-9-27 08:00:00
尚学堂AD

.Net在Framework4.0中增加了任务并行库,对开发人员来说利用多核多线程CPU环境变得更加简单,TPL正符合我们本系列的技术需求。因TPL涉及内容较多,且本系列文章为异步程序开发,所以本文并未涉及TPL全部内容。后续会写一个TPL系列的Blog,各位朋友可以关注一下。

TASK

TPL的基础Task,Task是TPL并行编程的最小单元,即表示一个异步操作。利用Task进行异步编程非常简单:

  1. static void Main(string[] args)
  2. {
  3. BaseTaskDemo();
  4. //BaseTaskDemo2();//两者效果相同
  5. Console.WriteLine(String.Format("Main 线程:{0},获取异步执行结果:小贝", Thread.CurrentThread.ManagedThreadId,task.Result));
  6. Console.ReadLine();
  7. }
  8. private static void BaseTaskDemo()
  9. {
  10. var task = new Task(() => {
  11. Thread.Sleep(2000);
  12. Console.WriteLine(String.Format("Task 线程:{0}", Thread.CurrentThread.ManagedThreadId));
  13. });
  14. task.Start();
  15. }
  16. private static void BaseTaskDemo2()
  17. {
  18. var task = Task.Run(() =>
  19. {
  20. Thread.Sleep(2000);
  21. Console.WriteLine(String.Format("Task 线程:{0}", Thread.CurrentThread.ManagedThreadId));
  22. });
  23. }
复制代码

程序说明:

1、new Task(Action)创建一个异步任务,参数Action是异步任务要执行的委托对象

2、task.Start()启动异步任务的执行

3、Task.Run(Action)等效于1、2的组合

上面的示例程序实现了异步操作,但主线程无法获知异步任务完成与否。为获取Task的执行结果,上面的程序进行如下修改:

  1. static void Main(string[] args)
  2. {
  3. var task = TaskForResult();
  4. Console.WriteLine(String.Format("Main 线程:{0}", Thread.CurrentThread.ManagedThreadId));
  5. task.Wait();
  6. if (task.IsCompleted)
  7. Console.WriteLine(String.Format("获取异步执行结果:{0}", task.Result));
  8. Console.ReadLine();
  9. }
  10. private static Task<int> TaskForResult()
  11. {
  12. var task = Task.Run(()=> {
  13. Thread.Sleep(2000);
  14. Console.WriteLine(string.Format("Task 线程:{0},Task执行完成。", Thread.CurrentThread.ManagedThreadId));
  15. return 10;
  16. });
  17. return task;
  18. }
复制代码

程序说明:

1、下面代码的原型为Task.Run(Func),Run的参数不再是Action,因为在该任务中我们要返回一个int值,所以应该使用Run(Func)这个重载。

  1. var task = Task.Run(()=> {
  2. Thread.Sleep(2000);
  3. Console.WriteLine(string.Format("Task 线程:{0},Task执行完成。", Thread.CurrentThread.ManagedThreadId));
  4. return 10;
  5. });
复制代码

2、主线程中调用task.Wait()时,主线程将一直等待异步任务完成或被取消。

3、task.IsCompleted属性用于判断异步任务是否完成

4、task.Result获取异步任务的执行结果(返回值)

上面的示例程序已经实现了主线程获取异步程序的状态及返回值,但如果异步程序非常耗时,则存在主线程需要临时取消耗时异步程序执行的功能。为了满足上述要求,程序可做如下调整:

  1. static void Main(string[] args)
  2. {
  3. CancellationTokenSource tokenSource = new CancellationTokenSource(5000);
  4. var task = TaskForResult2(tokenSource);
  5. Console.WriteLine(String.Format("Main 线程:{0}", Thread.CurrentThread.ManagedThreadId));
  6. Console.WriteLine(String.Format("Main 线程:{0},获取异步执行结果:小贝", Thread.CurrentThread.ManagedThreadId, task.Result));
  7. Console.ReadLine();
  8. }
  9. private static Task<int> TaskForResult2(CancellationTokenSource tokenSource)
  10. {
  11. var task = Task.Run(() =>
  12. {
  13. Thread.Sleep(10000);
  14. if (!tokenSource.IsCancellationRequested)
  15. {
  16. Console.WriteLine(String.Format("Task 线程:{0},任务1执行完成。", Thread.CurrentThread.ManagedThreadId));
  17. return 10;
  18. }
  19. else
  20. {
  21. return -1;
  22. }
  23. }, tokenSource.Token);
  24. return task;
  25. }
复制代码

程序说明:

1、CancellationTokenSource提供任务取消消息,构造参数 5000 表示CancellationTokenSource在发出5s后超时并取消

2、在Task的委托内部 tokenSource.IsCancellationRequested 获取取消标记

3、task.Result会隐式调用Wait()方法

如果异步Task在执行过程中出现异常,则需要对发生的异常做出响应:

  1. static void Main(string[] args)
  2. {
  3. CancellationTokenSource tokenSource = new CancellationTokenSource(5000);
  4. var task = TaskForResult2(tokenSource);
  5. Console.WriteLine(String.Format("Main 线程:{0}", Thread.CurrentThread.ManagedThreadId));
  6. try
  7. {
  8. //task.Wait();
  9. Console.WriteLine(String.Format("Main 线程:{0},获取异步执行结果:小贝", Thread.CurrentThread.ManagedThreadId, task.Result));
  10. }
  11. catch (AggregateException ex)
  12. {
  13. }
  14. Console.ReadLine();
  15. }
  16. private static Task<int> TaskForResult2(CancellationTokenSource tokenSource)
  17. {
  18. var task = Task.Run(() =>
  19. {
  20. Thread.Sleep(1000);
  21. if (!tokenSource.IsCancellationRequested)
  22. {
  23. throw new Exception("抛出异常");
  24. }
  25. else
  26. {
  27. return -1;
  28. }
  29. }, tokenSource.Token);
  30. return task;
  31. }
复制代码

程序说明:

1、在Task中引发的异常需要在 task.Wait()或task.Result时捕获

写在后面:Task的功能远不止上述这些,如Task多任务串行、TaskFactory、Paralle等知识非常有趣和重要。如果你感兴趣的话,可以关注本人后续TPL的文章。

复制代码


回复

使用道具 举报