2015-10-26 31 views
1

我有兩個例子,直接從微軟,這些例子似乎與取消令牌無關,因爲我可以刪除提供給該任務的令牌,結果是相同的。所以我的問題是:什麼是取消標記,爲什麼窮人的例子?我錯過了什麼..? :)向任務提供一個取消令牌不會做什麼?

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

*因爲我可以刪除提供給任務的令牌,結果是一樣的。*您看到了哪些結果? –

+0

將'Console.WriteLine(task.Status);'添加到您的代碼中。 – PetSerAl

+1

沒有令牌,第一個樣本永遠不會停止。 –

回答

3

由於取消在.NET是合作傳遞CancellationTokenTask.Run例如是不夠的,以確保任務被取消。

將令牌作爲參數傳遞只會將令牌與任務相關聯。如果在令牌被取消之前它沒有機會開始運行,它可以取消僅限於的任務。例如:

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

取消任務「飛行途中」你需要的任務本身,觀察令牌,當取消在信號扔,類似於:

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

此外,簡單地拋出一個來自任務內部的異常僅將任務標記爲Faulted。將其標記爲CancelledTaskCanceledException.CancellationToken需要匹配令牌傳遞給Task.Run

+0

但是,這正是第一個例子。我不認爲OP在這種情況下真的知道他應該期待什麼,因爲他所說的只是*,因爲我可以刪除提供給該任務的令牌,並且結果是相同的*。 –

+2

@YuvalItzchakov這些例子很好。這個答案解釋了爲什麼你需要「將令牌提供給任務」,即使結果*看起來是相同的。 – i3arnon

+1

是的,它確實回答了這個問題。 – MakerOfTheUnicake

0

我正要問類似的問題,直到找到這一個。 i3arnon的答案是有道理的,但我會添加這個答案作爲補充,希望可以幫助別人。

我會先說(與接受的答案中的評論形成對比),微軟在MSDN上的示例非常糟糕。除非你已經知道取消是如何工作的,否則他們不會幫助你。 This MSDN article向您展示瞭如何將CancellationToken轉換爲Task,但是如果您按照示例進行操作,他們將永遠不會告訴您如何取消您自己的正在執行Task。該CancellationToken剛剛消失在微軟的源代碼:我如何使用CancellationToken

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

下面舉例說明:

當我有一個需要不斷重複的任務:

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一些關鍵功能,但經過CancellationTokenTask其他什麼比國家從來沒有做出太大的意義我。我還沒有碰到,我已經通過了CancellationTokenTask和它的運行之前取消Task的情況下,即使我沒有,在每一個任務我創建的第一行總是

if (token.IsCancellationRequested) return;

相關問題