博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C#当中的多线程_任务并行库(上)
阅读量:7187 次
发布时间:2019-06-29

本文共 8654 字,大约阅读时间需要 28 分钟。

复习:

第三章内容中我们提到了三种异步编程模型,这里简单复习一下,分别如下

1.APM(异步编程模式):形如BeginxxxEndxxx

2.EAP(基于事件的异步编程模式):这个我们在.net中使用到了BackgroudWorker组件,使用方法是通过事件绑定处理的方式。

3.TPL(基于任务的异步编程模式):这个就会用到任务并行库。

 

4.1 简介

 

线程池相当于线程和用户之间的一个抽象层,向程序员隐藏了使用线程的细节,使得程序员专心处理程序逻辑,而不是各种线程问题。

但是使用线程池也很复杂。有两个问题存在:

①获取线程池中的工作线程的结果比较难

②实现线程池中工作线程执行的时序问题

综上,我们在第3章中提过的异步编程模型和基于事件的异步编程模型,这些模式使得获取结果更加容易,传播也更轻松,但是在进行多个异步操作组合的时候,还需要编写大量的代码。对于第二个问题.NET 4.0提出了一个新的关于异步操作的API。叫做任务并行库(Task Parallel Library 简称 TPL)。

 

TPL可以看成线程池之上的又一个抽象层,其对程序员隐藏了与线程池交互的底层代码,并提供了更方便的细粒度的API

TPL的核心概念是任务。一个任务代表了一个异步操作,该操作可以使用多种方式运行,可以使用或不使用独立线程运行。

一个任务可以有多种方式和其他任务组合起来。例如,可以同时执行多个任务,等待所有任务完成,然后运行一个任务对之前所有的任务结果进行一些计算。TPL与之前的模式相比,其中一个关键优势是其具有用于组合任务的便利的API

处理任务中的异常结果也有多种方式。一个任务可以由多种任务组成,这些任务也可以有各自的子任务,所以有一个AggregateException的概念。这种异常可以捕获底层任务内部的所有异常,并允许单独处理这些异常。

C#5.0中可以使用awaitasync关键词以平滑的,舒服的方式进行操作任务。

 

4.2 创建任务

创建任务有两种方式:

1.直接创建任务实例,通过实例方法Start方法来启动任务

2.使用静态方法Task.RunTask.Factory.StartNew来创建任务,两者都不需要显示的调用start方法启动任务,区别在于前者是后者的一种快捷方式,后者可以使用附加的选项。

例:1     class Program2     {3         static void Main(string[] args)4         {5             //第一种直接创建任务实例,需要用start方法来启动任务6             var t1 = new Task(() => TaskMethod("Task 1"));7             var t2 = new Task(() => TaskMethod("Task 2"));8             t2.Start();9             t1.Start();10           //第二种通过Task.Factory.StartNew来创建任务11           //这里Run方法只是Task.Factory.StartNew的一个快捷方式,Task.Factory.StartNew可以添加附加选项12           Task.Run(() => TaskMethod("Task 3"));13           Task.Factory.StartNew(() => TaskMethod("Task 4"));14           //我们标记了该任务是长时间任务,结果该任务没有使用线程池,而是在单独的线程中运行15           Task.Factory.StartNew(() => TaskMethod("Task 5"), TaskCreationOptions.LongRunning);16           Thread.Sleep(TimeSpan.FromSeconds(1));17         }18 19         static void TaskMethod(string name)20         {21             Console.WriteLine(22                                 "Task {0} is running on a thread id {1}. Is thread pool thread: {2}",23                                  name,24                                 Thread.CurrentThread.ManagedThreadId, 25                                 Thread.CurrentThread.IsThreadPoolThread);26         }27   }

※由于没有对任务的时序做处理,所以多次执行每一次都可能不一样。

Task5采用的是单独线程的方式来运行,但是根据运行该任务的当前的任务调度程序(task scheduler),运行方式可能会不同。

 

4.3使用任务执行基本的操作

主要介绍如何从任务中获取结果。

1     class Program2     {3         static void Main(string[] args)4         {5              //启动主线程6              TaskMethod("Main Thread Task");7              //创建一个任务Task1,进行线程的同步8              Task
task = CreateTask("Task 1");9              task.Start();10             //阻塞主线程,直到线程执行完成11             int result = task.Result;12             Console.WriteLine("Result is: {0}", result);13 14             //创建Taks2,使用RunSynchronously()方法进行同步15             task = CreateTask("Task 2");16             task.RunSynchronously();17             result = task.Result;18             Console.WriteLine("Result is: {0}", result);19 20             //创建Task3,此时不进行主线程的阻塞21             task = CreateTask("Task 3");22             Console.WriteLine(task.Status);23             task.Start();24 25             //循环打印task的状态,直到任务完成26             while (!task.IsCompleted)27             {28                 Console.WriteLine(task.Status);29                 Thread.Sleep(TimeSpan.FromSeconds(0.5));30             } 31             32             Console.WriteLine(task.Status);33             result = task.Result;34             Console.WriteLine("Result is: {0}", result);35         }36 37         //创建一个新任务38         static Task
CreateTask(string name)39         {40             return new Task
(() => TaskMethod(name));41         }42 43         //任务需要处理的方法44         static int TaskMethod(string name)45         {46             Console.WriteLine("Task {0} is running on a thread id {1}. Is thread pool thread: {2}",47             name, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);48             Thread.Sleep(TimeSpan.FromSeconds(2));49             return 42;50         }51 }

执行结果:

 

4.4 组合任务

这里我会学习到如何将任务进行组合,以及父子任务之间的执行。废话不说,有码

实例1:1     class Program2     {3         static void Main(string[] args)4         {5             //打印主线程6             TaskMethod("Main Task", 1);7             //创建两个任务8             var firstTask = new Task
(() => TaskMethod("First Task", 3));9             var secondTask = new Task
(() => TaskMethod("Second Task", 2));10 11             //设置firstTask的后续操作12             firstTask.ContinueWith(13                 t => Console.WriteLine("The first answer is {0}. Thread id {1}, is thread pool thread: {2}",14                     t.Result, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread),15                 TaskContinuationOptions.OnlyOnRanToCompletion);16 17              //启动两个任务18             firstTask.Start();19             secondTask.Start();20             //延时4秒,足够两个任务完成的时间※↓这句是关键21             Thread.Sleep(TimeSpan.FromSeconds(4));22 23             //为secondTask设置一个后续操作,TaskContinuationOptions.ExecuteSynchronously尝试同步方式执行后续操作24             Task continuation = secondTask.ContinueWith(25                 t => Console.WriteLine("The second answer is {0}. Thread id {1}, is thread pool thread: {2}",26                     t.Result, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread),27                 TaskContinuationOptions.OnlyOnRanToCompletion | TaskContinuationOptions.ExecuteSynchronously);28 29             //为之前的后续操作也定义一个后续操作,这里使用了C#5.0的方法GetAwaiter().OnCompleted()30             continuation.GetAwaiter().OnCompleted(31                 () => Console.WriteLine("Continuation Task Completed! Thread id {0}, is thread pool thread: {1}",32                     Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread));33 34             Thread.Sleep(TimeSpan.FromSeconds(2));35             Console.WriteLine();36 37             Thread.Sleep(TimeSpan.FromSeconds(10));38         }39 40         static int TaskMethod(string name, int seconds)41         {42             Console.WriteLine("Task {0} is running on a thread id {1}. Is thread pool thread: {2}",43                 name, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);44             Thread.Sleep(TimeSpan.FromSeconds(seconds));45             return 42 * seconds;46         }47 }

这里我们看到secondTask的后续操作没有使用到线程池,为什么呢?

解释:由上面的代码我们看到,使用了TaskContinuationOptions.ExecuteSynchronously尝试同步方式执行后续操作,如果后续操作时间非常短暂,使用上面的方式非常有效率的,因为放置在主线程进行运行要比放置在线程池中运行要快,那为啥会出现这样的情况呢,就是上面标记的延时代码的功劳,这段延时代码使得SecondTask后续操作正好得到了前面任务执行的结果。现在我把  Thread.Sleep(TimeSpan.FromSeconds(4));注释掉再试一下,结果如下:

感觉就像食堂打饭,两个人吃饭,AB打饭。

第一种是:A打完饭后,发现B刚来,就直接把饭给了B,然后B直接吃了

第二种是:A打饭的时候,B正好也来了,于是两人一起站队,A打完饭后再把饭给了B

 

2:演示了一下父子任务之间的关系。

1 class Program2     {3         static void Main(string[] args)4         {5              //创建一个父任务6              var firstTask = new Task
(() =>7             {8                 //创建一个子任务,使用TaskCreationOptions.AttachedToParent来标识9                 var innerTask = Task.Factory.StartNew(10                                         () => TaskMethod("Second Task", 5), 11                                         TaskCreationOptions.AttachedToParent);12               //创建一个子任务的后续操作,该后续操作也会影响父任务13                innerTask.ContinueWith(14                                         t => TaskMethod("Third Task", 2), 15                                         TaskContinuationOptions.AttachedToParent);16                 return TaskMethod("First Task", 2);17             });18 19             //启动任务20             firstTask.Start();21 22             //循环打印任务的状态23             while (!firstTask.IsCompleted)24             {25                 Console.WriteLine(firstTask.Status);26                 Thread.Sleep(TimeSpan.FromSeconds(0.5));27             }28             Console.WriteLine(firstTask.Status);29 30             Thread.Sleep(TimeSpan.FromSeconds(10));31         }32 33         static int TaskMethod(string name, int seconds)34         {35             Console.WriteLine("Task {0} is running on a thread id {1}. Is thread pool thread: {2}",36                 name, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);37             Thread.Sleep(TimeSpan.FromSeconds(seconds));38             return 42 * seconds;39         }

上面结果显示,父任务必须等待所有的子任务完成才能完成,但是看不出来他们是同步还是异步执行的。因为从First TaskSencod Task它们之间的运行时序上也看不出来他们是父亲执行完了再执行的子任务,所以我觉得把父任务的时间调长一点,这回我让父任务执行10s

修改:

   return TaskMethod("First Task", 2);    return TaskMethod("First Task", 10);

结果如下

这回显示的都是firstTaskRunning状态,所以应该能肯定父子之间默认情况下也是异步执行的。因为父任务必须要等子任务全结束才能完事。

 

 

转载于:https://www.cnblogs.com/dcz2015/p/5053459.html

你可能感兴趣的文章
2018世界杯决赛:谁的选择多谁就会赢球!
查看>>
程序员教你如何追女生
查看>>
哈夫曼树构造算法的正确性证明
查看>>
我谈Web程序难测试
查看>>
nginx日志按照天进行分割
查看>>
Networker 8.1异机恢复Oracle 11gR2
查看>>
shell实现文件名相同路径不同的批量复制
查看>>
五、性能监视(7)SQLDIAG
查看>>
多个无线AP间无线组网实例
查看>>
服务器架构之性能扩展-第四章(5)
查看>>
20款Notepad++插件下载和介绍
查看>>
Nagios调用Python程序控制微信公众平台发布报警信息
查看>>
安装 Laravel 框架
查看>>
正则表达式匹配不包含特殊子串的字符串(零宽断言的使用)
查看>>
大话nbu九(nbu异机备份恢复oracle)
查看>>
在WebStorm中使用git向github提交源代码
查看>>
启动与关闭Oracle rac数据库集群
查看>>
《统一沟通-微软-实战》-1-部署-基础环境-2-ADCS
查看>>
AIX下删除LV后的现场保护和数据恢复方案
查看>>
操作记录:在ubuntu16.04.1配置qemu-img,qemu-nbd
查看>>