为什么要将取消令牌传递给 TaskFactory.StartNew?

Why pass cancellation token to TaskFactory.StartNew?

除了最常见的仅使用 "action" 参数调用 TaskFactory.StartNew 的形式 (1) https://msdn.microsoft.com/en-us/library/dd321439(v=vs.110).aspx

我们还有一种方法接受额外的参数作为 "Cancelation Token" (2) https://msdn.microsoft.com/en-us/library/dd988458.aspx

我的问题是,为什么我们应该使用调用 (2) 而不是调用 (1)?

我的意思是,如果我不将 Cancellation Token 作为参数传递,MSDN 中第 (2) 页的示例也可以工作(因为可以从委托函数访问变量令牌。类似于:

var tokenSource = new CancellationTokenSource();
      var token = tokenSource.Token;
      var files = new List<Tuple<string, string, long, DateTime>>();

      var t = Task.Factory.StartNew( () => { string dir = "C:\Windows\System32\";
                                object obj = new Object();
                                if (Directory.Exists(dir)) {
                                   Parallel.ForEach(Directory.GetFiles(dir),
                                   f => {
                                           if (token.IsCancellationRequested)
                                              token.ThrowIfCancellationRequested();
                                           var fi = new FileInfo(f);
                                           lock(obj) {
                                              files.Add(Tuple.Create(fi.Name, fi.DirectoryName, fi.Length, fi.LastWriteTimeUtc));          
                                           }
                                      });
                                 }
                              }
                        ); //note that I removed the ", token" from here
      tokenSource.Cancel();

那么,当我将取消令牌传递给 Task.Factory.StartNew 时,下面会发生什么吗?

谢谢

会发生两件事。

  1. 如果在调用 StartNew 之前令牌已被取消,它将永远不会启动线程,任务将处于 Canceled 状态。
  2. 如果从任务内部引发 OperationCanceledException 并且该异常以与 StartNew 相同的标记传递,它将导致返回的任务进入 Cancelled 状态。如果与异常关联的令牌是不同的令牌,或者您没有在任务中传递令牌,将进入 Faulted 状态。

P.S. 你永远不应该调用 Task.Factory.StartNew 而不传入 TaskScheduler because if you don't it can easily cause you to run code on the UI thread that you expected to run on a background thread。除非您绝对需要使用 StartNew,否则请使用 Task.Run(Task.Run 具有与 StartNew.

相同的 CancellationToken 行为