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

简介: 原文:.NET 4 并行(多核)编程系列之三 从Task的取消 .NET 4 并行(多核)编程系列之三 从Task的取消     前言:因为Task是.NET 4并行编程最为核心的一个类,也我们在是在并行编程常常打交道的类,所以,对Task对全面的了解很有必要。
原文: .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. 创建组合的取消TaskToken

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

 

 

  系列文章链接:

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

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

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

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

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

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

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

 

 

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

 

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

 

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

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

 

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


  //  create the cancellation token source
CancellationTokenSource 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.调用TaskStart()方法。

 

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

 

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

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

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

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

 

    首先,进入第一个议题:

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

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

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

    代码如下:


img_405b18b4b6584ae338e0f6ecaf736533.gif 代码
  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

  代码如下:

  

img_405b18b4b6584ae338e0f6ecaf736533.gif 代码
         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  =   0 ; 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()方法被调用了,而且在这个委托的方法中我们可以做很多的事情,如通知用户取消操作发生了。

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

   

img_405b18b4b6584ae338e0f6ecaf736533.gif 代码
         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  =   0 ; 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属性对于这个属性的详细使用,在后续的文章中会深入的讲述,在这里主要知道一点就行了:CancellationTokenWaitOne()方法会阻止task的运行,只有CancellationTokencancel()方法被调用后,这种阻止才会释放。

 

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

 

img_405b18b4b6584ae338e0f6ecaf736533.gif 代码
         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  =   0 ; 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,当这个CancellationTokenCancel()方法调用的时候,使用了这个token的多个task都会被取消。

 

img_405b18b4b6584ae338e0f6ecaf736533.gif 代码
         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  =   0 ; i  <   int .MaxValue; i ++ )
                {
                    token.ThrowIfCancellationRequested();
                    Console.WriteLine(
" Task 1 - Int value {0} " , i);
                }
            }, token);

            Task task2 
=   new  Task(()  =>
            {
                
for  ( int  i  =   0 ; 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. 创建组合的取消TaskToken

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

 

img_405b18b4b6584ae338e0f6ecaf736533.gif 代码
         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是否已经被取消了

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

 

img_405b18b4b6584ae338e0f6ecaf736533.gif 代码
         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  =   0 ; i  <   10 ; 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  =   0 ; 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

 

 

目录
相关文章
|
存储 JSON 开发工具
Visual Studio编程效率提升技巧集(提高.NET编程效率)
Visual Studio编程效率提升技巧集(提高.NET编程效率)
215 0
Visual Studio编程效率提升技巧集(提高.NET编程效率)
|
11月前
|
传感器 数据采集 物联网
探索.NET nanoFramework:为嵌入式设备编程的新途径
探索.NET nanoFramework:为嵌入式设备编程的新途
413 7
|
11月前
.NET 4.0下实现.NET4.5的Task类相似功能组件
【10月更文挑战第29天】在.NET 4.0 环境下,可以使用 `BackgroundWorker` 类来实现类似于 .NET 4.5 中 `Task` 类的功能。`BackgroundWorker` 允许在后台执行耗时操作,同时不会阻塞用户界面线程,并支持进度报告和取消操作。尽管它有一些局限性,如复杂的事件处理模型和不灵活的任务管理方式,但在某些情况下仍能有效替代 `Task` 类。
144 0
|
大数据 开发工具 开发者
从零到英雄:.NET核心技术带你踏上编程之旅,构建首个应用,开启你的数字世界探险!
【8月更文挑战第28天】本文带领读者从零开始,使用强大的.NET平台搭建首个控制台应用。无论你是新手还是希望扩展技能的开发者,都能通过本文逐步掌握.NET的核心技术。从环境搭建到创建项目,再到编写和运行代码,详细步骤助你轻松上手。通过计算两数之和的小项目,你不仅能快速入门,还能为未来开发更复杂的应用奠定基础。希望本文为你的.NET学习之旅开启新篇章!
98 1
|
存储 C#
揭秘C#.Net编程秘宝:结构体类型Struct,让你的数据结构秒变高效战斗机,编程界的新星就是你!
【8月更文挑战第4天】在C#编程中,结构体(`struct`)是一种整合多种数据类型的复合数据类型。与类不同,结构体是值类型,意味着数据被直接复制而非引用。这使其适合表示小型、固定的数据结构如点坐标。结构体默认私有成员且不可变,除非明确指定。通过`struct`关键字定义,可以包含字段、构造函数及方法。例如,定义一个表示二维点的结构体,并实现计算距离原点的方法。使用时如同普通类型,可通过实例化并调用其成员。设计时推荐保持结构体不可变以避免副作用,并注意装箱拆箱可能导致的性能影响。掌握结构体有助于构建高效的应用程序。
422 7
|
Java Spring 自然语言处理
Spring 框架里竟藏着神秘魔法?国际化与本地化的奇妙之旅等你来揭开谜底!
【8月更文挑战第31天】在软件开发中,国际化(I18N)与本地化(L10N)对于满足不同地区用户需求至关重要。Spring框架提供了强大支持,利用资源文件和`MessageSource`实现多语言文本管理。通过配置日期格式和货币符号,进一步完善本地化功能。合理应用这些特性,可显著提升应用的多地区适应性和用户体验。
129 0
|
传感器 数据采集 物联网
探索未来:.NET nanoFramework引领嵌入式设备编程革新之旅
【8月更文挑战第28天】.NET nanoFramework 是一款专为资源受限的嵌入式设备设计的轻量级、高性能框架,基于 .NET Core,采用 C# 进行开发,简化了传统底层硬件操作的复杂性,极大提升了开发效率。开发者可通过 Visual Studio 或 Visual Studio Code 快速搭建环境并创建项目,利用丰富的库和驱动程序轻松实现从基础 LED 控制到网络通信等多种功能,显著降低了嵌入式开发的门槛。
242 0
|
JSON 编解码 Go
Golang深入浅出之-HTTP客户端编程:使用net/http包发起请求
【4月更文挑战第25天】Go语言`net/http`包提供HTTP客户端和服务器功能,简化高性能网络应用开发。本文探讨如何发起HTTP请求,常见问题及解决策略。示例展示GET和POST请求的实现。注意响应体关闭、错误处理、内容类型设置、超时管理和并发控制。最佳实践包括重用`http.Client`,使用`context.Context`,处理JSON以及记录错误日志。通过实践这些技巧,提升HTTP编程技能。
177 1
|
Go 开发者
Golang深入浅出之-HTTP客户端编程:使用net/http包发起请求
【4月更文挑战第24天】Go语言的`net/http`包在HTTP客户端编程中扮演重要角色,但使用时需注意几个常见问题:1) 检查HTTP状态码以确保请求成功;2) 记得关闭响应体以防止资源泄漏;3) 设置超时限制,避免长时间等待;4) 根据需求处理重定向。理解这些细节能提升HTTP客户端编程的效率和质量。
170 1
|
SQL 开发框架 安全
【.NET Core】深入理解任务并行库 (TPL)
【.NET Core】深入理解任务并行库 (TPL)
180 0