千家信息网

如何理解.NET 4并行编程中Task的取消

发表于:2024-11-23 作者:千家信息网编辑
千家信息网最后更新 2024年11月23日,如何理解.NET 4并行编程中Task的取消,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。因为Task是.NET 4并行
千家信息网最后更新 2024年11月23日如何理解.NET 4并行编程中Task的取消

如何理解.NET 4并行编程中Task的取消,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。

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

小编主要讲述如何取消一个task。

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

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

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

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

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

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

CancellationToken token = tokenSource.Token;

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

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

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();         }

看完上述内容是否对您有帮助呢?如果还想对相关知识有进一步的了解或阅读更多相关文章,请关注行业资讯频道,感谢您对的支持。

0