C# 使用Task执行异步操作
在C#4.0之前需要执行一个复杂的异步操作时,只能使用CLR线程池技术来执行一个任务。线程池执行异步任务时,不知道任务何时完成,以及任务的在任务完成后不能获取到返回值。但是在C#4.0中引人了一个的任务(System.Threading.Tasks命名空间的类型)机制来解决异步操作完成时间和完成后返回值的问题。
1.使用Task类创建并执行简单任务
通过使用Task的构造函数来创建任务,并调用Start方法来启动任务并执行异步操作。创建任务时,必须传递一个Action或Action<Object>类型的委托回调方法,可以选择的传递任务执行时说需要的数据对象等。Task类的构造函数如下:
public Task(Action action);public Task(Action<object> action, object state);public Task(Action action, CancellationToken cancellationToken);public Task(Action action, TaskCreationOptions creationOptions);public Task(Action<object> action, object state, CancellationToken cancellationToken);public Task(Action<object> action, object state, TaskCreationOptions creationOptions);public Task(Action action, CancellationToken cancellationToken, TaskCreationOptions creationOptions);public Task(Action<object> action, object state, CancellationToken cancellationToken, TaskCreationOptions creationOptions);
示例代码:
1: static void Main(string[] args)
2: {
3: Console.WriteLine("主线程执行业务处理.");
4: //创建任务
5: Task task = new Task(() => {
6: Console.WriteLine("使用System.Threading.Tasks.Task执行异步操作.");
7: for (int i = 0; i < 10; i++)
8: {
9: Console.WriteLine(i);
10: }
11: });
12: //启动任务,并安排到当前任务队列线程中执行任务(System.Threading.Tasks.TaskScheduler)
13: task.Start();
14: Console.WriteLine("主线程执行其他处理");
15: //主线程挂起1000毫秒,等待任务的完成。
16: Thread.Sleep(1000);
17: }
任务调度结果:
2.等待任务的完成并获取返回值
使用任务执行异步操作时,最主要的是要后的任务完成时的返回值。在任务类中有一个实例方法Wait(有许多重载版本)他能等待任务的完成,我们也可以通过Task类的派生类Task<TResult>创建一个异步任务,并指定任务完成时返回值的类型,这样可以通过Task<TResult>的实例对象获取到任务完成后的返回值。创建一个异步任务并执行0到100求和操作返回最后的计算结果,示例代码:
1: static void TaskWait() {
2: //创建任务
3: Task<int> task = new Task<int>(() =>
4: {
5: int sum = 0;
6: Console.WriteLine("使用Task执行异步操作.");
7: for (int i = 0; i < 100; i++)
8: {
9: sum+=i;
10: }
11: return sum;
12: });
13: //启动任务,并安排到当前任务队列线程中执行任务(System.Threading.Tasks.TaskScheduler)
14: task.Start();
15:
16: Console.WriteLine("主线程执行其他处理");
17: //等待任务的完成执行过程。
18: task.Wait();
19: //获得任务的执行结果
20: Console.WriteLine("任务执行结果:{0}", task.Result.ToString());
21: }
执行结果:
Task类还有一些静态方法,WaitAll用于等待提供的所有 System.Threading.Tasks.Task 对象完成执行过程和Wait用于等待提供的任一个 System.Threading.Tasks.Task 对象完成执行过程,这两个方法都有一些重载版本。
//等待所有任务完成
public static void WaitAll(params Task[] tasks);
//等待任意一个任务完成
public static int WaitAny(params Task[] tasks);
3.使用ContinueWith方法在任务完成时启动一个新任务
在使用能够Task类的Wait方法等待一个任务时或派生类的Result属性获得任务执行结果都有可能阻塞线程,为了解决这个问题可以使用ContinueWith方法,他能在一个任务完成时自动启动一个新的任务来处理执行结果。
示例代码:
1: static void TaskContinueWith()
2: {
3: //创建一个任务
4: Task<int> task = new Task<int>(() =>
5: {
6: int sum = 0;
7: Console.WriteLine("使用Task执行异步操作.");
8: for (int i = 0; i < 100; i++)
9: {
10: sum += i;
11: }
12: return sum;
13: });
14: //启动任务,并安排到当前任务队列线程中执行任务(System.Threading.Tasks.TaskScheduler)
15: task.Start();
16: Console.WriteLine("主线程执行其他处理");
17: //任务完成时执行处理。
18: Task cwt = task.ContinueWith(t => {
19: Console.WriteLine("任务完成后的执行结果:{0}", t.Result.ToString());
20: });
21: Thread.Sleep(1000);
22: }
执行结果:
上述示例中任务不是等待完成来显示执行结果,而是使用ContinueWith方法,它能够知道任务在什么时候完成并启动一个新的任务来执行任务完成后的处理。ContinueWith方法具有一些重载版本,这些重载版本允许指定延续任务需要使用的数据、延续任务的工作方式(System.Threading.Tasks.TaskContinuationOptions的枚举值按位OR运行的结果)等。
4.创建父子任务和任务工厂的使用
通过Task类创建的任务是顶级任务,可以通过使用 TaskCreationOptions.AttachedToParent 标识把这些任务与创建他的任务相关联,所有子任务全部完成以后父任务才会结束操作。示例如下:
1: static void ParentChildTask() {
2: Task<string[]> parent = new Task<string[]>(state => {
3: Console.WriteLine(state);
4: string[] result=new string[2];
5: //创建并启动子任务
6: new Task(() => { result[0]= "我是子任务1。"; },TaskCreationOptions.AttachedToParent).Start();
7: new Task(() => { result[1] = "我是子任务2。"; }, TaskCreationOptions.AttachedToParent).Start();
8: return result;
9: },"我是父任务,并在我的处理过程中创建多个子任务,所有子任务完成以后我才会结束执行。");
10: //任务处理完成后执行的操作
11: parent.ContinueWith(t => {
12: Array.ForEach(t.Result, r=>Console.WriteLine(r));
13: });
14: //启动父任务
15: parent.Start();
16: Console.Read();
17: }
执行结果:
如果需要创建一组具有相同状态的任务时,可以使用TaskFactory类或TaskFactory<TResult>类。这两个类创建一组任务时可以指定任务的CancellationToken、TaskCreationOptions、TaskContinuationOptions和TaskScheduler默认值。示例代码:
1: static void TaskFactoryApply()
2: {
3: Task parent = new Task(() =>
4: {
5: CancellationTokenSource cts = new CancellationTokenSource(5000);
6: //创建任务工厂
7: TaskFactory tf = new TaskFactory(cts.Token, TaskCreationOptions.AttachedToParent, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
8: //添加一组具有相同状态的子任务
9: Task[] task = new Task[]{
10: tf.StartNew(() => { Console.WriteLine("我是任务工厂里的第一个任务。"); }),
11: tf.StartNew(() => { Console.WriteLine("我是任务工厂里的第二个任务。"); }),
12: tf.StartNew(() => { Console.WriteLine("我是任务工厂里的第三个任务。"); })
13: };
14: });
15: parent.Start();
16: Console.Read();
17: }
执行结果:
5.任务内部实现和任务调度
任务内部有一组构成任务状态的属性,标识任务的唯一Id、表示任务的执行状态(TaskStatus)、任务创建时提供的回调函数的引用和传递给回调函数的数据对象AsyncState、对任务创建时的任务调度对象(TaskScheduler)的引用、对父任务的引用以及对执行上下文的引用和ManualResetEventSlim对象的引用。Task类和Task<TResult>类都实现了标准的释放资源的接口,允许在任务完成处理的时候使用Dispose方法释放资源(关闭ManualResetEventSlim对象实例)。可以使用Task类的CurrentId属性获得正在执行的任务的Id,如果没有任务在执行CurrentId返回值为null,CurrentId是一个int?可空类型的属性。任务执行的生命周期通过TaskStatus类型的一个值来表示,TaskStatus所包含的值:
public enum TaskStatus{Created = 0,WaitingForActivation = 1,WaitingToRun = 2,Running = 3,WaitingForChildrenToComplete = 4,RanToCompletion = 5,Canceled = 6,Faulted = 7,}
我们可以通过Task类的Exception属性获得任务在执行过程中的所有异常 ,Exception是一个AggregateException类型的属性。Task类提供了IsCanceled、IsCompleted、IsFaulted属性来获得任务的完成状态。通过ContinueWith、ContinueWhenAll、ContinueWhenAny和FromAsync创建的后续任务都处于WaitingForActivation 状态,这个状态的任务会在父任务完成后自动执行。
在任务内部由TaskScheduler类调度任务的执行,该类是一个抽象类,FCL中从他派生了两个派生类:ThreadPoolTaskScheduler线程池任务调度器和SynchronizationContextTaskScheduler同步上下文任务调度器。所有任务默认都是采用ThreadPoolTaskScheduler调度任务,他是采用线程池来执行任务,可以通过TaskScheduler类的静态属性Default获得对默认任务调度器的引用。SynchronizationContextTaskScheduler任务调度器能够用在Window form、WPF等应用程序,他的任务调度是采用的GUI线程,所以他能同步更新UI组件,可以通过TaskScheduler类的静态方法FromCurrentSynchronizationContext获得对一个同步上下文任务调度起的引用。
任务调度示例:
1: private void button1_Click(object sender, EventArgs e)
2: {
3: //获得同步上下文任务调度器
4: TaskScheduler m_syncContextTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
5:
6: //创建任务,并采用默认任务调度器(线程池任务调度器)执行任务
7: Task<int> task = new Task<int>(() =>
8: {
9: //执行复杂的计算任务。
10: Thread.Sleep(2000);
11: int sum = 0;
12: for (int i = 0; i < 100; i++)
13: {
14: sum += i;
15: }
16: return sum;
17: });
18: var cts=new CancellationTokenSource();
19: //任务完成时启动一个后续任务,并采用同步上下文任务调度器调度任务更新UI组件。
20: task.ContinueWith(t => {this.label1.Text="采用SynchronizationContextTaskScheduler任务调度器更新UI。\r\n计算结果是:"+task.Result.ToString(); },
21: cts.Token ,TaskContinuationOptions.AttachedToParent,m_syncContextTaskScheduler);
22: task.Start();
23: }
执行结果:
本文简单的介绍了使用Task类来执行异步操作以及任务的内部实现与任务调度。在执行复杂异步操作时,可以采用任务来执行,他能更好的知道异步操作在何时完成以及返回异步操作的执行结果。
C# 使用Task执行异步操作相关推荐
- C# 线程知识--使用Task执行异步操作(转)
http://www.codingthink.com/c/20121223/201212232246371.html 在C#4.0之前需要执行一个复杂的异步操作时,只能使用CLR线程池技术来执行一个任 ...
- spark executor task执行
Executor执行任务的起点是Executor的launchTask()方法. val executorData = executorDataMap(task.executorId) executo ...
- Spark修炼之道(高级篇)——Spark源码阅读:第九节 Task执行成功时的结果处理...
Task执行成功时的结果处理 在上一节中,给出了Task在Executor上的运行代码演示,我们知道代码的最终运行通过的是TaskRunner方法 class TaskRunner(execBacke ...
- C#Task执行线程及其相关问题
对于多线程,我们经常使用的是Thread.在我们了解Task之前,如果我们要使用多核的功能可能就会自己来开线程,然而这种线程模型在.net 4.0之后被一种称为基于"任务的编程模型" ...
- Ansible的task执行方式控制:forks以及serial
Contents 1. Ansible执行task的方式:广度优先 2. Ansible执行task的方式:深度优先 2.1. serial的其他用法 3. References Ansible执行t ...
- Android 跟踪 Gradle Task 执行
https://blog.csdn.net/sk719887916/article/details/84191771 作者:Tamic Gradle 大家并不陌生,但我相信你会使用它配置一些依赖,编写 ...
- 第37课 : Task执行内幕与结果处理解密
第37课 : Task执行内幕与结果处理解密 Task执行及结果处理原理流程图和源码解密: 在Standalone模式中,Driver中的CoarseGrainedSchedulerBackend给 ...
- verilog中关于always语句嵌套task执行顺序和@(posedge clk)执行方式的问题
首先明确一个事实 always@(posedge clk)中的任务没有执行完的情况下是不会在下一个时钟上升沿到来的时候再次重复执行的 再明确另一个事实 在前面不带always的情况下,@(posedg ...
- NET 提供了执行异步操作的三种模式
1.APM模式简介 在.net1.x的版本中就可以使用IAsyncResult接口实现异步操作,但是比较复杂,这种称之为异步编程模型模式 (Asynchronous Programming Model ...
最新文章
- Pycharm开发环境设置与熟悉。
- 二分图的判定(模板)
- boost::mpl模块实现list_c相关的测试程序
- 【网络通信与信息安全】之深入解析进程之间的通信方式
- Python函数式编程简介(二)返回函数
- navicat 导出的sql文件,再导入,运行SQL文件成功,数据库中却没有表
- mysql必知必会--用通配符进行过滤
- Centos 6.2出现Disk sda contains BIOS RAID metadata解决方法
- 【CAD】自定义实体的步骤(转)
- #Android Studio# 模拟器开发者模式
- SSRF漏洞理解进阶SSRF+gopher打内网(redis、mysql、fastcgi) SSRF相关基础概念
- 给定一个设备编号区间[start, end],包含4或18的编号都不能使用,如:418、148、718不能使用,108可用
- 用idea将一个java文件打包成可执行jar包并能正确运行
- linux-使用screen后台运行命令,防止断网导致异常退出,命令没运行完成
- 如何在Linux系统中将无线网卡配置为AP
- (单击)双击直接修改内容的代码
- STM32 CubeMX学习:7. ADC模数转化
- 计算机网络和因特网大二暑假
- MySql的exists
- 当网站关键词优化到搜索引擎首页后,是否还要继续优化呢?
热门文章
- 信息学奥赛一本通(1249:Lake Counting)
- 一半的一半(51Nod-2382)
- 训练日志 2019.4.6
- 训练日志 2019.1.26
- 八皇后问题 (信息学奥赛一本通-T1213)
- ftp响应码以及解释说明是服务器返回,FTP命令字和响应码解释
- 文件md5码怎么生成_Linux 系统文件校验方法--MD5,SHA1,PGP,SHA256,SHA512
- CentOS虚拟机时间同步
- 使用EndNote X9引用参考文献并在Word中修改生成的引文格式(编号、字体大小)GBT7714(numeric)
- [Unity] GameFramework 学习记录 3