为一个任务提供一个取消资源什么都不做?

时间:2015-10-26 09:36:58

标签: c# task-parallel-library cancellation-token

我有两个例子,直接来自microsoft,这些示例似乎与取消令牌无关,因为我可以删除提供给任务的令牌,结果是相同的。所以我的问题是:什么是取消令牌,以及为什么这些可怜的例子?我错过了什么......? :)

using System;
using System.Threading;
using System.Threading.Tasks;
namespace Chapter1.Threads
{
    public class Program
    {
        static void Main()
        {
            CancellationTokenSource cancellationTokenSource =
                new CancellationTokenSource();
            CancellationToken token = cancellationTokenSource.Token;
            Task task = Task.Run(() =>
            {
                while (!token.IsCancellationRequested)
                {
                    Console.Write(“*”);
                    Thread.Sleep(1000);
                }
                token.ThrowIfCancellationRequested();
            }, token);
            try
            {
                Console.WriteLine(“Press enter to stop the task”);
                Console.ReadLine();
                cancellationTokenSource.Cancel();
                task.Wait();
            }  
            catch (AggregateException e)
            {
                Console.WriteLine(e.InnerExceptions[0].Message);
            }
            Console.WriteLine(“Press enter to end the application”);
            Console.ReadLine();
        }
    }
}

代码示例2: https://msdn.microsoft.com/en-us/library/system.threading.cancellationtoken(v=vs.110).aspx

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

public class Example
{
   public static void Main()
   {
      // Define the cancellation token.
      CancellationTokenSource source = new CancellationTokenSource();
      CancellationToken token = source.Token;

      Random rnd = new Random();
      Object lockObj = new Object();

      List<Task<int[]>> tasks = new List<Task<int[]>>();
      TaskFactory factory = new TaskFactory(token);
      for (int taskCtr = 0; taskCtr <= 10; taskCtr++) {
         int iteration = taskCtr + 1;
         tasks.Add(factory.StartNew( () => {
                                       int value;
                                       int[] values = new int[10];
                                       for (int ctr = 1; ctr <= 10; ctr++) {
                                          lock (lockObj) {
                                             value = rnd.Next(0,101);
                                          }
                                          if (value == 0) { 
                                             source.Cancel();
                                             Console.WriteLine("Cancelling at task {0}", iteration);
                                             break;
                                          }   
                                          values[ctr-1] = value; 
                                       }
                                       return values;
                                    }, token));   

      }
      try {
         Task<double> fTask = factory.ContinueWhenAll(tasks.ToArray(), 
                                                      (results) => {
                                                         Console.WriteLine("Calculating overall mean...");
                                                         long sum = 0;
                                                         int n = 0; 
                                                         foreach (var t in results) {
                                                            foreach (var r in t.Result) {
                                                                  sum += r;
                                                                  n++;
                                                               }
                                                         }
                                                         return sum/(double) n;
                                                      } , token);
         Console.WriteLine("The mean is {0}.", fTask.Result);
      }   
      catch (AggregateException ae) {
         foreach (Exception e in ae.InnerExceptions) {
            if (e is TaskCanceledException)
               Console.WriteLine("Unable to compute mean: {0}", 
                                 ((TaskCanceledException) e).Message);
            else
               Console.WriteLine("Exception: " + e.GetType().Name);
         }
      }
      finally {
         source.Dispose();
      }
   }
}

2 个答案:

答案 0 :(得分:3)

由于.Net中的取消是合作传递,例如CancellationToken进入Task.Run是不足以确保任务被取消。

将令牌作为参数传递仅将令牌与任务相关联。如果在取消令牌之前没有机会开始,它可以仅取消任务。例如:

var token = new CancellationToken(true); // creates a cancelled token
Task.Run(() => {}, token);

要取消“飞行途中”的任务,您需要任务本身观察令牌并在发出取消信号时抛出,类似于:

Task.Run(() => 
{
    while (true)
    {
        token.ThrowIfCancellationRequested();
        // do something
    }
}, token);

此外,只是从任务内部抛出异常只会将任务标记为Faulted。要将其标记为Cancelled TaskCanceledException.CancellationToken需要与传递给Task.Run的令牌匹配。

答案 1 :(得分:0)

在我找到这个问题之前,我正要问一个类似的问题。来自i3arnon的答案是有道理的,但我会将这个答案添加为有希望帮助某人的补充。

我首先要说的是(与接受的答案的评论相反)微软在MSDN上的例子非常糟糕。除非您已经知道取消如何运作,否则它们对您没有多大帮助。 This MSDN article向您展示了如何将CancellationToken传递给Task,但如果您按照示例操作,他们实际上从未向您展示如何取消当前正在执行的您自己的 { {1}}。 Task只是消失在Microsoft代码中:

  • CancellationToken
  • await client.GetAsync("http://msdn.microsoft.com/en-us/library/dd470362.aspx", ct);

以下是我如何使用await response.Content.ReadAsByteArrayAsync();

的示例

当我有一项需要不断重复的任务时:

CancellationToken

当我有一个用户可以启动的任务时:

public class Foo
{
    private CancellationTokenSource _cts;

    public Foo()
    {
        this._cts = new CancellationTokenSource();
    }

    public void StartExecution()
    {
        Task.Factory.StartNew(this.OwnCodeCancelableTask, this._cts.Token);
        Task.Factory.StartNew(this.OwnCodeCancelableTask_EveryNSeconds, this._cts.Token);
    }

    public void CancelExecution()
    {
        this._cts.Cancel();
    }

    /// <summary>
    /// "Infinite" loop with no delays. Writing to a database while pulling from a buffer for example.
    /// </summary>
    /// <param name="taskState">The cancellation token from our _cts field, passed in the StartNew call</param>
    private void OwnCodeCancelableTask(object taskState)
    {
        var token = (CancellationToken) taskState;

        while ( !token.IsCancellationRequested )
        {
            Console.WriteLine("Do your task work in this loop");
        }
    }


    /// <summary>
    /// "Infinite" loop that runs every N seconds. Good for checking for a heartbeat or updates.
    /// </summary>
    /// <param name="taskState">The cancellation token from our _cts field, passed in the StartNew call</param>
    private async void OwnCodeCancelableTask_EveryNSeconds(object taskState)
    {
        var token = (CancellationToken)taskState;

        while (!token.IsCancellationRequested)
        {
            Console.WriteLine("Do the work that needs to happen every N seconds in this loop");

            // Passing token here allows the Delay to be cancelled if your task gets cancelled.
            await Task.Delay(1000 /*Or however long you want to wait.*/, token);
        }
    }
}

也许我错过了public class Foo { private CancellationTokenSource _cts; private Task _taskWeCanCancel; public Foo() { this._cts = new CancellationTokenSource(); //This is where it's confusing. Passing the token here will only ensure that the task doesn't //run if it's canceled BEFORE it starts. This does not cancel the task during the operation of our code. this._taskWeCanCancel = new Task(this.FireTheTask, this._cts.Token); } /// <summary> /// I'm not a fan of returning tasks to calling code, so I keep this method void /// </summary> public void FireTheTask() { //Check task status here if it's required. this._taskWeCanCancel.Start(); } public void CancelTheTask() { this._cts.Cancel(); } /// <summary> /// Go and get something from the web, process a piece of data, execute a lengthy calculation etc... /// </summary> private async void OurTask() { Console.WriteLine("Do your work here and check periodically for task cancellation requests..."); if (this._cts.Token.IsCancellationRequested) return; Console.WriteLine("Do another step to your work here then check the token again if necessary..."); if (this._cts.Token.IsCancellationRequested) return; Console.WriteLine("Some work that we need to delegate to another task"); await Some.Microsoft.Object.DoStuffAsync(); } } 的一些关键功能,但将Task传递给CancellationToken,因为除了州以外的其他任何内容对我来说都没有多大意义。我还没遇到过这样的情况,我已经Task传递给CancellationToken并在运行之前取消了Task,即使我这样做了,每个任务中的第一行我创造总是

Task