zoukankan      html  css  js  c++  java
  • 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

        代码如下:


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

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

       

    代码
            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 ");
                });

                
    // 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的一种方法了。

     

    代码
            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都会被取消。

     

    代码
            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就会被取消。代码如下:

     

    代码
            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是否被取消了。代码如下:

     

    代码
            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

     
  • 相关阅读:
    NodeJs 的Module.export 和 export
    Angular 调试
    设计模式 -- 访问者
    typescript 枚举
    Swagger 实践 <二>
    eventFlow 系列 <三> 查询所有
    成员变量的隐藏和方法的重写
    Facetoprocess_program_design
    ATM_tests
    transmission protocol
  • 原文地址:https://www.cnblogs.com/owenzh/p/11014190.html
Copyright © 2011-2022 走看看