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

2 に答える 2

3

.Net でのキャンセルは協調的であるため、たとえばにaCancellationTokenを渡すだけでは、タスクがキャンセルされたことを確認するのに十分ではありません。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.CancellationTokenTask.Run

于 2015-10-26T09:48:52.030 に答える
0

これを見つけるまで、私は同様の質問をしようとしていました。i3arnon からの回答は理にかなっていますが、うまくいけば誰かを助けるために、この回答を追加します。

(受け入れられた回答のコメントとは対照的に) MSDN の Microsoft の例はひどいものだと言うことから始めます。キャンセルがどのように機能するかを既に知っていない限り、それらはあまり役に立ちません。この MSDN の記事でCancellationTokenは、 aを aに渡す方法が示されていますが、例に従えば、現在実行中の をキャンセルするTask方法が実際に示されることはありません。Microsoft コードに消えてしまいます。TaskCancellationToken

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

}

のいくつかの重要な機能を見逃したのかもしれませんが、状態以外のものとしてを aにTask渡すことは、私にはあまり意味がありませんでした。aに aを渡して実行前にキャンセルしたという状況にまだ遭遇したことがありません。実行したとしても、作成するすべてのタスクの最初の行は常にCancellationTokenTaskCancellationTokenTaskTask

if (token.IsCancellationRequested) return;

于 2016-03-11T17:04:35.830 に答える