原文:.NET 4 并行(多核)编程系列之三 从Task的取消

.NET 4 并行(多核)编程系列之三 从Task的取消

  前言:因为Task是.NET 4并行编程最为核心的一个类,也我们在是在并行编程常常打交道的类,所以,对Task对全面的了解很有必要。

  上篇文章主要讲述了如何创建一个task,本篇文章主要讲述如何取消一个task。

  本篇主的主要议题如下:

1.         1. 通过轮询的方式检测Task是否被取消

2.        2.  用委托delegate来检测Task是否被取消

3.         3. 用Wait Handle还检测Task是否被取消

4.         4. 取消多个Task

5.         5. 创建组合的取消Task的Token

6.         6. 判断一个Task是否已经被取消了

  系列文章链接:

  .NET 4 并行(多核)编程系列之一入门介绍

  .NET 4 并行(多核)编程系列之二 从Task开始

  .NET 4 并行(多核)编程系列之三 从Task的取消

  .NET 4 并行(多核)编程系列之四 Task的休眠

  .NET 并行(多核)编程系列之五 Task执行和异常处理

  .NET 并行(多核)编程系列之六 Task基础部分完结篇

  .NET 并行(多核)编程系列之七 共享数据问题和解决概述

本篇的理论不多,代码的例子很多。

在TPL中一个标准化的操作就是”取消Task”。之所以说它是个标准化的操作,其实是把这个操作和之前传统的多线程编程进行比较而言的。

在之前的多线程编程中,我们一般是自己写一些代码来取消线程的运行。但是在.NET 4的TPL中就内置了取消的方法,可能我们觉得TPL没有必要内置这些代码,因为太简单了。但是这个内置的方法不仅仅只是取消了运行的Task,而且还减小了在取消运行的Task时可能产生的一些风险,我们后续文章会详细讲述。

创建一个取消的Task一般要进行下面一些步骤:

a.           a.创建System.Threading.CancellationTokenSource的一个实例:

 // create the cancellation token sourceCancellationTokenSource tokenSource = new CancellationTokenSource();

b.        b.通过CancellationTokenSource.Token属性获得一个System.Threading.CancellationToken:

    

CancellationToken token = tokenSource.Token;

    c.创建一个新的Task或者Task<T>,并且在构造函数传入Action或者Action<object>的委托作为第一个参数,传入CancellationToken作为第二个参数:

Task task = new Task(new Action(printMessage), token);

d.        d.调用Task的Start()方法。

    上面的步骤和我们之前介绍的创建一个Task的代码几乎一样,只是在构造函数中多传入了一个参数。

    如果想要取消一个Task的运行,只要调用CancellationToken实例的Cancel()方法就可以了。

    有点要特别注意的,当我们调用了Cancel()方法之后,.NET Framework不会强制性的去关闭运行的Task。

    我们自己必须去检测之前在创建Task时候传入的那个CancellationToken。

    我们在创建Task是传入CancellationToken到构造函数,其实这个CancellationToken就是.NET Framework用来避免我们再次运行已经被取消的Task,可以说就是一个标志位。

  首先,进入第一个议题:

  1.       通过轮询的方式检测Task是否被取消

  在很多Task内部都包含了循环,用来处理数据。我们可以在循环中通过CancellationToken的IsCancellationRequest属性来检测task是否被取消了。如果这个属性为true,那么我们就得跳出循环,并且释放task所占用的资源(如数据库资源,文件资源等).

  我们也可以在task运行体中抛出System.Threading.OperationCanceledException来取消运行的task。

    代码如下:

代码

 while (true)
{
    if (token.IsCancellationRequested)
    {
          // tidy up and release resources
           throw new OperationCanceledException(token);
    }
     else
     {
        // do a unit of work
       }
 }

  如果我们没有任何的资源要释放,那么只要简单的调用CancellationToken.ThrowIfCancellationRequested()方法,这个方法会检查是否要取消task,并且抛出异常。代码如下:

 while (true){        token.ThrowIfCancellationRequested();         // do a unit of work }

  下面就给出有一个完整的例子:创建一个可以取消的task,并且通过轮询不断的检查是否要取消task

  代码如下:

  

代码

        static void Main(string[] args)
        {
            // create the cancellation token source
            CancellationTokenSource tokenSource = new CancellationTokenSource();

            // create the cancellation token
            CancellationToken token = tokenSource.Token;
            // create the task

            Task task = new Task(() =>
            {
                for (int i = ; i < int.MaxValue; i++)
                {
                    if (token.IsCancellationRequested)
                    {
                        Console.WriteLine("Task cancel detected");
                        throw new OperationCanceledException(token);
                    }
                    else
                    {
                        Console.WriteLine("Int value {0}", i);
                    }
                }
            }, token);

            // wait for input before we start the task
            Console.WriteLine("Press enter to start task");
            Console.WriteLine("Press enter again to cancel task");
            Console.ReadLine();

            // start the task
            task.Start();

            // read a line from the console.
            Console.ReadLine();

            // cancel the task
            Console.WriteLine("Cancelling task");
            tokenSource.Cancel();

            // wait for input before exiting
            Console.WriteLine("Main method complete. Press enter to finish.");
            Console.ReadLine();
        }

  2.       用委托delegate来检测Task是否被取消

  我们可以在注册一个委托到CancellationToken中,这个委托的方法在CancellationToken.Cancel()调用之前被调用。

  我们可以用这个委托中的方法来作为一个检测task是否被取消的另外一个可选的方法,因为这个方法是在Cancel()方法被调用之前就调用的,所以这个委托中的方法可以检测task是否被cancel了,也就是说,只要这个委托的方法被调用,那么就说这个CancellationToken.Cancel()方法被调用了,而且在这个委托的方法中我们可以做很多的事情,如通知用户取消操作发生了。

  下面的代码给出了一个例子。

代码

        static void Main(string[] args)
        {
            // create the cancellation token source
            CancellationTokenSource tokenSource = new CancellationTokenSource();

            // create the cancellation token
            CancellationToken token = tokenSource.Token;

            // create the task
            Task task = new Task(() =>
            {
                for (int i = ; i < int.MaxValue; i++)
                {
                    if (token.IsCancellationRequested)
                    {
                        Console.WriteLine("Task cancel detected");
                        throw new OperationCanceledException(token);
                    }
                    else
                    {
                        Console.WriteLine("Int value {0}", i);
                    }
                }
            }, token);

            // register a cancellation delegate
            token.Register(() =>
            {
                Console.WriteLine(">>>>>> Delegate Invoked\n");
            });

            // wait for input before we start the task
            Console.WriteLine("Press enter to start task");
            Console.WriteLine("Press enter again to cancel task");
            Console.ReadLine();

            // start the task
            task.Start();
            // read a line from the console.
            Console.ReadLine();

            // cancel the task
            Console.WriteLine("Cancelling task");
            tokenSource.Cancel();

            // wait for input before exiting
            Console.WriteLine("Main method complete. Press enter to finish.");
            Console.ReadLine();
        }

  3.       用Wait Handle还检测Task是否被取消

  第三种方法检测task是否被cancel就是调用CancellationToken.WaitHandle属性。对于这个属性的详细使用,在后续的文章中会深入的讲述,在这里主要知道一点就行了:CancellationToken的WaitOne()方法会阻止task的运行,只有CancellationToken的cancel()方法被调用后,这种阻止才会释放。

  在下面的例子中,创建了两个task,其中task2调用了WaitOne()方法,所以task2一直不会运行,除非调用了CancellationToken的Cancel()方法,所以WaitOne()方法也算是检测task是否被cancel的一种方法了。

代码

        static void Main(string[] args)
        {

            // create the cancellation token source
            CancellationTokenSource tokenSource = new CancellationTokenSource();

            // create the cancellation token
            CancellationToken token = tokenSource.Token;

            // create the task
            Task task1 = new Task(() =>
            {
                for (int i = ; i < int.MaxValue; i++)
                {
                    if (token.IsCancellationRequested)
                    {
                        Console.WriteLine("Task cancel detected");
                        throw new OperationCanceledException(token);
                    }
                    else
                    {
                        Console.WriteLine("Int value {0}", i);
                    }
                }
            }, token);

            // create a second task that will use the wait handle
            Task task2 = new Task(() =>
            {
                // wait on the handle
                token.WaitHandle.WaitOne();
                // write out a message
                Console.WriteLine(">>>>> Wait handle released");
            });

            // wait for input before we start the task
            Console.WriteLine("Press enter to start task");
            Console.WriteLine("Press enter again to cancel task");
            Console.ReadLine();
            // start the tasks
            task1.Start();
            task2.Start();

            // read a line from the console.
            Console.ReadLine();

            // cancel the task
            Console.WriteLine("Cancelling task");
            tokenSource.Cancel();

            // wait for input before exiting
            Console.WriteLine("Main method complete. Press enter to finish.");
            Console.ReadLine();
        }

  4.      取消多个Task

  我们可以使用一个CancellationToken来创建多个不同的Tasks,当这个CancellationToken的Cancel()方法调用的时候,使用了这个token的多个task都会被取消。

代码

        static void Main(string[] args)
        {
            // create the cancellation token source
            CancellationTokenSource tokenSource = new CancellationTokenSource();

            // create the cancellation token
            CancellationToken token = tokenSource.Token;

            // create the tasks
            Task task1 = new Task(() =>
            {
                for (int i = ; i < int.MaxValue; i++)
                {
                    token.ThrowIfCancellationRequested();
                    Console.WriteLine("Task 1 - Int value {0}", i);
                }
            }, token);

            Task task2 = new Task(() =>
            {
                for (int i = ; i < int.MaxValue; i++)
                {
                    token.ThrowIfCancellationRequested();
                    Console.WriteLine("Task 2 - Int value {0}", i);
                }
            }, token);
            // wait for input before we start the tasks
            Console.WriteLine("Press enter to start tasks");
            Console.WriteLine("Press enter again to cancel tasks");
            Console.ReadLine();

            // start the tasks
            task1.Start();
            task2.Start();

            // read a line from the console.
            Console.ReadLine();

            // cancel the task
            Console.WriteLine("Cancelling tasks");
            tokenSource.Cancel();
            // wait for input before exiting
            Console.WriteLine("Main method complete. Press enter to finish.");
            Console.ReadLine();
        }

  5. 创建组合的取消Task的Token

  我们可以用CancellationTokenSource.CreateLinkedTokenSource()方法来创建一个组合的token,这个组合的token有很多的CancellationToken组成。主要组合token中的任意一个token调用了Cancel()方法,那么使用这个组合token的所有task就会被取消。代码如下:

代码

        static void Main(string[] args)
        {
            // create the cancellation token sources
            CancellationTokenSource tokenSource1 = new CancellationTokenSource();
            CancellationTokenSource tokenSource2 = new CancellationTokenSource();
            CancellationTokenSource tokenSource3 = new CancellationTokenSource();

            // create a composite token source using multiple tokens
            CancellationTokenSource compositeSource = 
                CancellationTokenSource.CreateLinkedTokenSource(
            tokenSource1.Token, tokenSource2.Token, tokenSource3.Token);

            // create a cancellable task using the composite token
            Task task = new Task(() =>
            {
                // wait until the token has been cancelled
                compositeSource.Token.WaitHandle.WaitOne();
                // throw a cancellation exception
                throw new OperationCanceledException(compositeSource.Token);
            }, compositeSource.Token);

            // start the task
            task.Start();

            // cancel one of the original tokens
            tokenSource2.Cancel();

            // wait for input before exiting
            Console.WriteLine("Main method complete. Press enter to finish.");
            Console.ReadLine();
        }

  6.      判断一个Task是否已经被取消了

  可以使用Task的IsCancelled属性来判断task是否被取消了。代码如下:

代码

        static void Main(string[] args)
        {
            // create the cancellation token source
            CancellationTokenSource tokenSource1 = new CancellationTokenSource();

            // create the cancellation token
            CancellationToken token1 = tokenSource1.Token;

            // create the first task, which we will let run fully
            Task task1 = new Task(() =>
            {
                for (int i = ; i < ; i++)
                {
                    token1.ThrowIfCancellationRequested();
                    Console.WriteLine("Task 1 - Int value {0}", i);
                }
            }, token1);

            // create the second cancellation token source
            CancellationTokenSource tokenSource2 = new CancellationTokenSource();

            // create the cancellation token
            CancellationToken token2 = tokenSource2.Token;

            // create the second task, which we will cancel
            Task task2 = new Task(() =>
            {
                for (int i = ; i < int.MaxValue; i++)
                {
                    token2.ThrowIfCancellationRequested();
                    Console.WriteLine("Task 2 - Int value {0}", i);
                }
            }, token2);

            // start all of the tasks
            task1.Start();
            task2.Start();

            // cancel the second token source
            tokenSource2.Cancel();
            // write out the cancellation detail of each task
            Console.WriteLine("Task 1 cancelled? {0}", task1.IsCanceled);
            Console.WriteLine("Task 2 cancelled? {0}", task2.IsCanceled);
            // wait for input before exiting
            Console.WriteLine("Main method complete. Press enter to finish.");
            Console.ReadLine();
        }

今天就写到这里,比较的简单,都是一些很基础的东西,只有这样,后面深入讲解的时候才更加的顺利。

谢谢各位!

   版权为小洋和博客园所有,转载请标明出处给作者。

   http://www.cnblogs.com/yanyangtian

Task的取消的更多相关文章

  1. .NET 4 并行(多核)编程系列之三 从Task的取消

    原文:.NET 4 并行(多核)编程系列之三 从Task的取消 .NET 4 并行(多核)编程系列之三 从Task的取消 前言:因为Task是.NET 4并行编程最为核心的一个类,也我们在是在并行编程 ...

  2. Task:取消异步计算限制操作 & 捕获任务中的异常

    Why:ThreadPool没有内建机制标记当前线程在什么时候完成,也没有机制在操作完成时获得返回值,因而推出了Task,更精确的管理异步线程. How:通过构造方法的参数TaskCreationOp ...

  3. Task加入取消功能

      参考:http://www.cnblogs.com/scy251147/archive/2013/01/04/2843875.html static void TaskWithCancellati ...

  4. .NET 4并行编程入门之Task的取消[转]

    原文:http://www.cnblogs.com/Leo_wl/archive/2010/06/01/1749596.html前言:因为Task是.NET 4并行编程最为核心的一个类,也我们在是在并 ...

  5. .Net多线程编程—任务Task

    1 System.Threading.Tasks.Task简介 一个Task表示一个异步操作,Task的创建和执行是独立的. 只读属性: 返回值 名称 说明 object AsyncState 表示在 ...

  6. .NET 同步与异步之封装成Task(五)

    本随笔续接:.NET 实现并行的几种方式(四) 前篇随笔已经介绍了几种可以实现并发的方式,其中异步方法.是最简便的方式.而 异步方式是基于 Task 和 async修饰符和await运算符实现的. 换 ...

  7. c# .Net并行和多线程编程之Task学习记录!

    任务Task和线程Thread的区别: 1.任务是架构在线程之上的,也就是说任务最终还是要抛给线程去执行. 2.任务跟线程不是一对一的关系,比如开10个任务并不是说会开10个线程,这一点任务有点类似线 ...

  8. Task C# 多线程和异步模型 TPL模型

    Task,异步,多线程简单总结 1,如何把一个异步封装为Task异步 Task.Factory.FromAsync 对老的一些异步模型封装为Task TaskCompletionSource 更通用, ...

  9. 5天玩转C#并行和多线程编程 —— 第三天 认识和使用Task

    5天玩转C#并行和多线程编程系列文章目录 5天玩转C#并行和多线程编程 —— 第一天 认识Parallel 5天玩转C#并行和多线程编程 —— 第二天 并行集合和PLinq 5天玩转C#并行和多线程编 ...

随机推荐

  1. Linux 相关系统日志查看

    1. 登录日志 cat /var/log/secure 涉及到账号登录的日志信息都会记录在此文件中. 2. Unit 的启动日志 journalctl 可以查看所有 unit 的启动日志,日志的配置文 ...

  2. 邻居子系统 之 邻居项创建__neigh_create

    概述 IP层输出数据包会根据路由的下一跳查询邻居项,如果不存在则会调用__neigh_create创建邻居项,然后调用邻居项的output函数进行输出: __neigh_create完成邻居项的创建, ...

  3. CentOS7 开机启动脚本与命令后台运行

    一.& 在 Linux 命令后加上 &  可以在后台运行 二.nohup 对 SIGHUP 信号免疫,对 SIGINT 信号不免疫,可用 shopt | grep hup 查看. 当关 ...

  4. Php+Redis函数使用总结

    因项目需求,冷落了redis,今天再重新熟悉一下: <?php //连接 $redis = New Redis(); $redis->connect('127.0.0.1','6379', ...

  5. LC 856. Score of Parentheses

    Given a balanced parentheses string S, compute the score of the string based on the following rule: ...

  6. LC 272. Closest Binary Search Tree Value II 【lock,hard】

    Given a non-empty binary search tree and a target value, find k values in the BST that are closest t ...

  7. 阶段3 3.SpringMVC·_04.SpringMVC返回值类型及响应数据类型_6 响应json数据之过滤静态资源

    先搭建环境 webapp目录下创建js的文件夹.然后里面引入jquery.min.js这个文件. 页面引入这个js文件 先alert弹窗做测试 服务器重新部署 点击ajax的按钮 并没有起作用 我们在 ...

  8. (转)java8实现对象列表去重

    java8实现列表去重,java8的stream和lambda的使用实例 通过普通的方式也可以达到去重的效果,但是借助java8新特性可以很方便的实现列表去重,测试demo如下 实体类: public ...

  9. docker commit命令

    docker commit命令用于基于一个容器来创建一个新的docker镜像. docker commit制作的镜像,除了制定镜像的人知道执行过什么命令,怎么生成的镜像,别人根本无从得知.建议使用的是 ...

  10. 模拟SQLserver IO压力测试 工具编 SQLIOSIM

    描述 最近有业务需求需了解客户的服务器SQLserver 的IO情况,而不仅仅是通过系统计数器 了解硬盘的IO情况或者使用CrystalDiskMark或者Trace重播进行压力测试 .这时SQL S ...