C# 多线程六之Task(任务)二

前面介绍了Task的由来,以及简单的使用,包括开启任务,处理任务的超时、异常、取消、以及如果获取任务的返回值,在回去返回值之后,立即唤起新的线程处理返回值、且如果前面的任务发生异常,唤起任务如果有效的处理异常等关于Task的知识。所以本文将介绍Task更多的用法和特性.

一、如果通过一个任务创建多个子任务.

1、Task支持一个任务,创建多个子任务,并且保持关联.

        static void Main(string[] args)
        {
            var parentTask = new Task<int[]>(() =>
            {
                //开启多个子任务
                var results = new int[2];

                //创建子任务,并将子任务的值赋给results变量,并通过TaskCreationOptions.AttachedToParent,将其关联到父任务,如果不指定,该任务将独立于父任务单独执行
                //这里有个奇怪的问题,只能使用new Task的方式去创建关联到父任务的子任务,因为Task.Run没有提供这个方法,可以通过扩展方法解决这个问题
                new Task(() => results[0] = ChildThreadOne(), TaskCreationOptions.AttachedToParent).Start();
                new Task(() => results[1] = ChildThreadTwo(), TaskCreationOptions.AttachedToParent).Start();

                return results;
            });
            parentTask.Start();
            parentTask.ContinueWith(x =>
            {
                Console.WriteLine("当父任务执行完毕时,CLR会唤起一个新线程,将父任务的返回值(子任务的返回值)输出,所以这里不会有任何的线程发生阻塞");
                foreach (var re in parentTask.Result)
                {
                    Console.WriteLine("子任务的返回值分别为:{0}", re);
                }
            });
            Console.WriteLine("主线程不会阻塞,它会继续执行");
            Console.ReadKey();//必须加这行代码,因为Task时线程池线程,属于后台线程
        }

        /// <summary>
        /// 子任务一
        /// </summary>
        static int ChildThreadOne()
        {
            Thread.Sleep(2000);//模拟长时间计算操作
            Console.WriteLine("子任务一完成了计算任务,并返回值:{0}", 6);
            return 6;
        }

        /// <summary>
        /// 子任务一
        /// </summary>
        static int ChildThreadTwo()
        {
            Thread.Sleep(2000);//模拟长时间计算操作
            Console.WriteLine("子任务二完成了计算任务,并返回值:{0}", 6);
            return 6;
        }

二、关于Task的资源释放问题.

如果你看过Task的源码,你会发现下面这个有趣的问题:

ok,你会想它想释放什么呢?

没错,当Task任务,指定了TaskContinuationOptions枚举状态,且指定的值如下:

那么,直接return,什么资源释放操作都不做.

如果任务没有完成,就调用Dispose方法,那么直接抛异常,如果完成了,它就释放了ManualResetEventSlim信号量(后面的文章会介绍).所以如果你在task中使用了其它的一些非托管资源,那么最好在代码里自己手动释放,在使用完之后。或者自己实现了Task的派生类,把需要用的非托管资源加进去,然后在使用完派生类之后,调用Dispose方法.

三、关于Task的几个常用属性

1、Id属性,每个Task对象都有一个Id属性,全局唯一,且每次创建新的任务,这个值都会递增1.

2、TaskStatus状态

    //
    // 摘要:
    //     表示 System.Threading.Tasks.Task 的生命周期中的当前阶段。
    public enum TaskStatus
    {
        //
        // 摘要:
        //     该任务已初始化,但尚未被计划。
        Created = 0,
        //
        // 摘要:
        //     该任务正在等待 .NET Framework 基础结构在内部将其激活并进行计划。
        WaitingForActivation = 1,
        //
        // 摘要:
        //     该任务已被计划执行,但尚未开始执行。
        WaitingToRun = 2,
        //
        // 摘要:
        //     该任务正在运行,但尚未完成。
        Running = 3,
        //
        // 摘要:
        //     该任务已完成执行,正在隐式等待附加的子任务完成。
        WaitingForChildrenToComplete = 4,
        //
        // 摘要:
        //     已成功完成执行的任务。
        RanToCompletion = 5,
        //
        // 摘要:
        //     该任务已通过对其自身的 CancellationToken 引发 OperationCanceledException 对取消进行了确认,此时该标记处于已发送信号状态;或者在该任务开始执行之前,已向该任务的
        //     CancellationToken 发出了信号。 有关详细信息,请参阅任务取消。
        Canceled = 6,
        //
        // 摘要:
        //     由于未处理异常的原因而完成的任务。
        Faulted = 7
    }

构造完Task对象是,状态为Created,当任务启动时,状态变为WaitingToRun,当Task实际在线程上运行时,状态变为Running.如果当前任务为父任务,且它已经执行完毕,等待其它子任务执行完毕的时候,其状态变为WaitingForChildrenToComplete.如果任务完成可能会出现以下几种状态:RanToCompletion(已成功完成执行的任务)、Canceled(取消状态)、

Faulted(任务出错).

这里需要注意一个特殊的状态WaitingForActivation

当使用Task对象的ContinueWith的Task对象处理改状态,意味者该Task任务的调度由任务基础结构控制.也就是该任务的调度只有当前面的任务执行完之后,由CLR发起执行调用.

原文地址:https://www.cnblogs.com/GreenLeaves/p/10051830.html

时间: 2024-11-02 14:08:31

C# 多线程六之Task(任务)二的相关文章

多线程(六)线程间的通信和协作

系统要实现某个全局功能必定要需要各个子模块之间的协调和配合,就像一个团队要完成某项任务的时候需要团队各个成员之间密切配合一样.而对于系统中的各个子线程来说,如果要完成一个系统功能,同样需要各个线程的配合,这样就少不了线程之间的通信与协作.常见的线程之间通信方式有如下几种: 1.wait和notify/notifyAll  2.await和signal/signalAll   3.sleep/yield/join  4.CyclicBarrier 栅栏  5.CountDownLatch 闭锁  

微信公众平台开发教程(六)获取个性二维码

微信公众平台开发教程(六)获取个性二维码 一.功能介绍 在进行推广时,我们可以告诉对方,我们的微信公众账号是什么,客户可以去搜索,然后关注.二维码给我们提供了极大的便捷,只要简单一扫描,即可关注. 如果已经关注过,立刻跳入对话画面.在我们进行推广时,不再是简陋的文字,可以是一个有个性的二维码,想必会很生动. 微信对二维码提供了很好的支持,而且还可以根据需要生成不同场景的二维码.下面我们将介绍如何获取和使用二维码. 注意:限服务号,且进行了微信认证,费用300 二.相关接口 为了满足用户渠道推广分

使用Java开发多线程端口扫描工具(二)

一 介绍 这一篇文章是紧接着上一篇文章(http://www.zifangsky.cn/2015/12/使用java开发多线程端口扫描工具/)写的,端口扫描的原理不用多少,我在上一篇文章中已经说过了,至于目的大家都懂得.在这一篇文章里,我主要是对端口扫描工具的继续完善,以及写出一个比较直观的图形界面出来,以方便我们测试使用.界面如下: 这个工具主要是实现了以下几点功能:(1)两种扫描方式,一种是只扫描常见端口,另一种是设置一个起始和结束端口,依次探测.当然,原理很简单,用for循环就可以了:(2

C++学习笔记十六-模板和泛型编程(二)

C++学习笔记十六-模板和泛型编程(二) 16.4 类模板成员 1.模板作用域中模板类型的引用: 通常,当使用类模板的名字的时候,必须指定模板形参.这一规则有个例外:在类本身的作用域内部,可以使用类模板的非限定名.例如,在默认构造函数和复制构造函数的声明中,名字 Queue 是 Queue<Type> 缩写表示.实质上,编译器推断,当我们引用类的名字时,引用的是同一版本.因此,复制构造函数定义其实等价于: Queue<Type>(const Queue<Type> &a

从PRISM开始学WPF(六)MVVM(二)Command?

原文:从PRISM开始学WPF(六)MVVM(二)Command? 从PRISM开始学WPF(一)WPF? 从PRISM开始学WPF(二)Prism? 从PRISM开始学WPF(三)Prism-Region? 从PRISM开始学WPF(四)Prism-Module? 从PRISM开始学WPF(五)MVVM(一)ViewModel? 从PRISM开始学WPF(六)MVVM(二)Command? 从PRISM开始学WPF(七)MVVM(三)事件聚合器EventAggregator? 命令绑定(Com

.net 多线程 Thread ThreadPool Task

先准备一个耗时方法 /// <summary>/// 耗时方法/// </summary>/// <param name="name"></param>private void DoSomeThing(string name){                 Console.WriteLine($"开始执行{name}, {Thread.CurrentThread.ManagedThreadId.ToString("

Elasticsearch入门教程(六):Elasticsearch查询(二)

原文:Elasticsearch入门教程(六):Elasticsearch查询(二) 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/vbirdbest/article/details/79237950 地理坐标点geo-point 地理坐标点:是指地球表面可以用经纬度描述的一个点. 地理坐标点可以用来计算两个坐标间的距离,还可以判断一个坐标是否在一个区域中,或在聚合中. 地理坐标点不能被

Java多线程技术学习笔记(二)

目录: 线程间的通信示例 等待唤醒机制 等待唤醒机制的优化 线程间通信经典问题:多生产者多消费者问题 多生产多消费问题的解决 JDK1.5之后的新加锁方式 多生产多消费问题的新解决办法 sleep和wait的区别 停止线程的方式 守护线程 线程的其他知识点 一.线程间的通信示例 返目录回 多个线程在处理同一资源,任务却不同. 假设有一堆货物,有一辆车把这批货物往仓库里面运,另外一辆车把前一辆车运进仓库的货物往外面运.这里货物就是同一资源,但是两辆车的任务却不同,一个是往里运,一个是往外运. 下面

MySql学习(六) —— 数据库优化理论(二) —— 查询优化技术

逻辑查询优化包括的技术 1)子查询优化  2)视图重写  3)等价谓词重写  4)条件简化  5)外连接消除  6)嵌套连接消除  7)连接消除  8)语义优化 9)非SPJ优化 一.子查询优化 1. 什么是子查询:当一个查询是另一个查询的子部分时,称之为子查询. 2. 查询的子部分,包含的情况: a) 目标列位置:子查询如果位于目标列,则只能是标量子查询,否则数据库可能返回类似“错误:子查询只能返回一个字段 ( [Err] 1242 - Subquery returns more than 1