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方法添加回调函数

猜你喜欢

转载自www.cnblogs.com/GreenLeaves/p/9980979.html