zoukankan      html  css  js  c++  java
  • .NET 4 并行(多核)编程系列之三

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

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

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

    在之前的多线程编程中,我们一般是自己写一些代码来取消线程的运行。但是在.NET 4的TPL中就内置了取消的方法,可能我们觉得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.调用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 = 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\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 = 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,当这个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 = 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. 创建组合的取消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 = 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

  • 相关阅读:
    Top 10 Product Manager Skills To Boost Your Resume In 2021
    大数据知识梳理
    B端产品如何设计权限系统?
    华三盒式交换机MAC、ARP、Route性能表项参数查询
    中了传说中的挖矿病毒
    SqlServer 2019 事务日志传送
    docker中生成的pdf中文是方框的解决方案
    The Live Editor is unable to run in the current system configuration
    2021 面试题大纲
    五分钟搞定Docker安装ElasticSearch
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/1749596.html
Copyright © 2011-2022 走看看