C# 多线程学习系列四之取消、超时子线程操作

1、简介

虽然ThreadPool、Thread能开启子线程将一些任务交给子线程去承担,但是很多时候,因为某种原因,比如子线程发生异常、或者子线程的业务逻辑不符合我们的预期,那么这个时候我们必须关闭它,而不是让它继续执行,消耗资源.让CPU不在把时间和资源花在没有意义的代码上.

2、主线程取消所有子线程执行的简单代码演示和原理分析

(1)、代码演示

        static void Main(string[] args)
        {
            //显示定义一个取消辅助线程的操作
            CancellationTokenSource ctsToken = new CancellationTokenSource();
            ThreadPool.QueueUserWorkItem(o => EoworkOne(ctsToken.Token));
            ThreadPool.QueueUserWorkItem(o => EoworkTwo(ctsToken.Token));
            ctsToken.Cancel();
            Console.Read();
        }

        /// <summary>
        /// 辅助线程一
        /// </summary>
        /// <param name="token"></param>
        static void EoworkOne(CancellationToken token)
        {
            //判断主线程是否调用了CancellationTokenSource实例的Cancel方法
            //相当于判断主线程是否传递给辅助线程一一个取消标记
            if (token.IsCancellationRequested)
            {
                //如果主线程传递给辅助线程一一个取消操作标记,执行下面的代码
                Console.WriteLine("主线程调用了Cancel方法,所以辅助线程一获取了主线程取消辅助线程一的标记,但是并不会真正的关闭当前线程");
                Console.WriteLine("辅助线程一执行return操作,自己显示的退出,那么接下去的方法都不会被执行");
                return;
            }
        }

        /// <summary>
        /// 辅助线程二
        /// </summary>
        /// <param name="token"></param>
        static void EoworkTwo(CancellationToken token)
        {
            //判断主线程是否调用了CancellationTokenSource实例的Cancel方法
            //相当于判断主线程是否传递给辅助线程一一个取消标记
            if (token.IsCancellationRequested)
            {
                //如果主线程传递给辅助线程一一个取消操作标记,执行下面的代码
                Console.WriteLine("主线程调用了Cancel方法,所以辅助线程二获取了主线程取消辅助线程二的标记,但是并不会真正的关闭当前线程");
            }
            //因为当主线程传递给辅助线程二一个取消标记,但是上面的if语句块,并没有执行return操作,所以下面的语句还是会继续执行
            Console.WriteLine("辅助线程二获得取消标记操作后,并没有执行显示的return操作,所以辅助线程二继续执行");
        }

(2)、原理分析

 第一步:创建一个CancellationTokenSource对象实例,该对象包含了所有关于取消子线程有关的所有状态

CancellationTokenSource ctsToken = new CancellationTokenSource();

 第二步:将CancellationTokenSource对象实例的CancellationToken对象实例传递给需要进行取消操作的所有子线程.并且可以通过这个CancellationToken对象实例关联到CancellationTokenSource对象实例.

ThreadPool.QueueUserWorkItem(o => EoworkOne(ctsToken.Token));
ThreadPool.QueueUserWorkItem(o => EoworkTwo(ctsToken.Token));

 第三步:当主线程调用CancellationTokenSource对象实例的Cancel方法,所有的子线程通过调用CancellationToken对象实例的IsCancellationRequested属性,该属性定时去获取初始线程(主线程)是否执行了CancellationTokenSource对象实例的Cancel方法,如果调用了,该属性为true。这时可以理解为子线程到主线程的取消信号,可以通过调用return方法来终止子线程的操作.

   //判断主线程是否调用了CancellationTokenSource实例的Cancel方法
   //相当于判断主线程是否传递给辅助线程一一个取消标记
   if (token.IsCancellationRequested)
   {
       //如果主线程传递给辅助线程一一个取消操作标记,执行下面的代码
       Console.WriteLine("主线程调用了Cancel方法,所以辅助线程一获取了主线程取消辅助线程一的标记,但是并不会真正的关闭当前线程");
       Console.WriteLine("辅助线程一执行return操作,自己显示的退出,那么接下去的方法都不会被执行");
       return;
   }

3、如果创建一个不能被取消的子线程

通过给子线程传递一个CancellationToken.None实例,该子线程无法被取消,原因很简单,CancellationToken.None实例没有关联的CancellationTokenSource对象实例,所以无法调用Cancel方法显示取消.所以子线程调用token.IsCancellationRequested属性,该属性永远为false.调用token.CanBeCanceled属性也为false.

        static void Main(string[] args)
        {
            ThreadPool.QueueUserWorkItem(o => EoworkOne(CancellationToken.None));
            Console.Read();
        }

        /// <summary>
        /// 辅助线程一
        /// </summary>
        /// <param name="token"></param>
        static void EoworkOne(CancellationToken token)
        {
            if (token.IsCancellationRequested)
            {
                //永远无法执行
            }
            Console.WriteLine("辅助线程一能被取消吗?{0}",token.CanBeCanceled?"能":"不能");
            Console.WriteLine("通过CancellationToken.None实例创建的子线程无法被取消");
        }

4、初始线程(主线程)调用给CancellationTokenSource对象实例的Cancel方法添加回调函数

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Python攻城狮

Django教程(三)- Django表单Form1.Form 基本使用2.Form中字段及插件3.通过Django表单Form来完成需求4.自定义验证验证规则

创建Form类时,主要涉及到 【字段】 和 【插件】,字段用于对用户请求数据的验证,插件用于自动生成HTML;

21540
来自专栏小灰灰

Java 动手写爬虫: 二、 深度爬取

第二篇 前面实现了一个最基础的爬取单网页的爬虫,这一篇则着手解决深度爬取的问题 简单来讲,就是爬了一个网页之后,继续爬这个网页中的链接 1. 需求背景 背景...

768100
来自专栏GreenLeaves

EF 数据库连接约定(Connection String Conventions in Code First)

一个典型的EF应用大多数情况下是一个DbContext的派生类(derived class)来控制,通常可以使用该派生类调用DbContext的构造函数,来控制...

21390
来自专栏五毛程序员

五毛的cocos2d-x学习笔记07-计时器、数据读写、文件读写

26950
来自专栏pangguoming

C# 事件(Event)

事件(Event) 基本上说是一个用户操作,如按键、点击、鼠标移动等等,或者是一些出现,如系统生成的通知。应用程序需要在事件发生时响应事件。例如,中断。事件是用...

38150
来自专栏Unity

检测Unity客户端无效的预设资源

4、不被代码间接引用,如预设Anniver_01,在代码中是"Anniver_"+number,这种暂时没有好办法解决。

27340
来自专栏青玉伏案

iOS逆向工程之Hopper中的ARM指令

虽然前段时间ARM被日本软银收购了,但是科技是无国界的,所以呢ARM相关知识该学的学。现在看ARM指令集还是倍感亲切的,毕竟大学里开了ARM这门课,并且做了不少...

34970
来自专栏逸鹏说道

C# 温故而知新: 线程篇(二) 下

首先介绍下Classic Async Pattern: 其实Classic Async Pattern指的就是我们常见的BeginXXX和EndXXX IAsy...

29670
来自专栏向上的小草

mybati缓存机制之一级缓存

  在月黑风高的某天夜晚,boss chen语重心长的发条了消息给小草说:“小草啊,是时候写写博客来记录平常自己积累的东西了......”。小草一听,平常没有写...

28900
来自专栏名山丶深处

springboot集成jpa

17170

扫码关注云+社区

领取腾讯云代金券