Как использовать свойство CancellationToken?

По сравнению с предыдущим кодом для класса RulyCanceler, я хотел запустить код с помощью CancellationTokenSource.

Как я могу использовать его, как указано в Tokensation Tokens, т.е. не бросать/ловить исключение? Можно ли использовать свойство IsCancellationRequested?

Я попытался использовать его следующим образом:

cancelToken.ThrowIfCancellationRequested();

и

try
{
  new Thread(() => Work(cancelSource.Token)).Start();
}
catch (OperationCanceledException)
{
  Console.WriteLine("Canceled!");
}

но это дало ошибку времени выполнения cancelToken.ThrowIfCancellationRequested(); в методе Work(CancellationToken cancelToken):

System.OperationCanceledException was unhandled
  Message=The operation was canceled.
  Source=mscorlib
  StackTrace:
       at System.Threading.CancellationToken.ThrowIfCancellationRequested()
       at _7CancellationTokens.Token.Work(CancellationToken cancelToken) in C:\xxx\Token.cs:line 33
       at _7CancellationTokens.Token.<>c__DisplayClass1.<Main>b__0() in C:\xxx\Token.cs:line 22
       at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ThreadHelper.ThreadStart()
  InnerException:

Код, который я успешно выполнил, заставил OperationCanceledException в новом потоке:

using System;
using System.Threading;
namespace _7CancellationTokens
{
  internal class Token
  {
    private static void Main()
    {
      var cancelSource = new CancellationTokenSource();
      new Thread(() =>
      {
         try
         {
           Work(cancelSource.Token); //).Start();
         }
         catch (OperationCanceledException)
         {
            Console.WriteLine("Canceled!");
         }
         }).Start();

      Thread.Sleep(1000);
      cancelSource.Cancel(); // Safely cancel worker.
      Console.ReadLine();
    }
    private static void Work(CancellationToken cancelToken)
    {
      while (true)
      {
        Console.Write("345");
        cancelToken.ThrowIfCancellationRequested();
      }
    }
  }
}

Ответ 1

Вы можете реализовать свой метод работы следующим образом:

private static void Work(CancellationToken cancelToken)
{
    while (true)
    {
        if(cancelToken.IsCancellationRequested)
        {
            return;
        }
        Console.Write("345");
    }
}

Что это. Вам всегда нужно самостоятельно обрабатывать отмену - выйти из метода, когда наступает подходящее время для выхода (чтобы ваша работа и данные были в состоянии согласованы)

ОБНОВЛЕНИЕ: Я предпочитаю не писать while (!cancelToken.IsCancellationRequested), потому что часто есть несколько точек выхода, где вы можете прекратить безопасное выполнение в цикле цикла, и цикл обычно имеет некоторое логическое условие для выхода (перебирать все элементы в коллекции и т.д.). Поэтому я считаю, что лучше не смешивать эти условия, поскольку они имеют разные намерения.

Ответ 2

Вы можете использовать его следующим образом:

  var cancelToken = new CancellationTokenSource();
  Task.Factory.StartNew(() => DoExternalWork(), cancelToken.Token);

  Thread.Sleep(1000); //simulate some other work

  //this stops the Task:
  cancelToken.Cancel(false);

Ответ 3

@BrainSlugs83

Вы не должны слепо доверять всему, что было отправлено в stackoverflow. Комментарий в коде Йенса неверен, параметр не контролирует, выбрасываются ли исключения или нет.

MSDN очень ясно, что этот параметр контролирует, прочитал ли вы его? http://msdn.microsoft.com/en-us/library/dd321703(v=vs.110).aspx

Если throwOnFirstException истинно, исключение будет немедленно разворачиваться из вызова Отмена, предотвращая оставшиеся обратные вызовы и операции отмены транзакции. Если throwOnFirstException ложно, эта перегрузка будет агрегировать любые исключения, исключенные из исключения AggregateException, так что один обратный вызов бросание исключения не помешает другим зарегистрированным обратным вызовам Выполняется.

Имя переменной также неверно, потому что Cancel вызывается в CancellationTokenSource, а не сам токен, а источник изменяет состояние каждого маркера, которым он управляет.

Ответ 4

Вы можете использовать ThrowIfCancellationRequested без обработки исключения!

Использование ThrowIfCancellationRequested предназначено для использования в Задаче (а не в Потоке). При использовании внутри Задачи вам не нужно обрабатывать исключение самостоятельно (и получать ошибку Unhandled Exception). Это приведет к выходу из Task, и свойство Task.IsCancelled будет иметь значение True. Не требуется обработка исключений.

В вашем конкретном случае измените поток на задачу.

try
{
  Task.Run(() => Work(cancelSource.Token), cancelSource.Token);
}
if (t.IsCancelled)
  Console.WriteLine("Canceled!");
}

Ответ 5

Вы можете создать задачу с токеном отмены, когда вы переходите в фоновый режим приложения, вы можете отменить этот токен.

Вы можете сделать это в PCL https://developer.xamarin.com/guides/xamarin-forms/application-fundamentals/app-lifecycle.

var cancelToken = new CancellationTokenSource();
Task.Factory.StartNew(async () => {
    await Task.Delay(10000);
    // call web API
}, cancelToken.Token);

//this stops the Task:
cancelToken.Cancel(false);

Другим решением является пользовательский таймер в Xamarin.Forms, остановите таймер, когда приложение перейдет в фоновый режим https://xamarinhelp.com/xamarin-forms-timer/

Ответ 6

Вы должны передать CancellationToken Задаче, которая будет периодически контролировать токен, чтобы увидеть, запрашивается ли отмена.

CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
CancellationToken token = cancellationTokenSource.Token;  
Task task = Task.Run(() => {     
  while(!token.IsCancellationRequested) {
      Console.Write("*");         
      Thread.Sleep(1000);
  }
}, token);
Console.WriteLine("Press enter to stop the task"); 
Console.ReadLine(); 
cancellationTokenSource.Cancel(); 

В этом случае операция завершится при запросе отмены, и у Task будет состояние RanToCompletion. Если вы хотите получить подтверждение того, что ваша задача была отменена, вы должны использовать ThrowIfCancellationRequested чтобы ThrowIfCancellationRequested исключение OperationCanceledException.

Task task = Task.Run(() =>             
{                 
    while (!token.IsCancellationRequested) {
         Console.Write("*");                      
        Thread.Sleep(1000);                 
    }           
    token.ThrowIfCancellationRequested();               
}, token)
.ContinueWith(t =>
 {
      t.Exception?.Handle(e => true);
      Console.WriteLine("You have canceled the task");
 },TaskContinuationOptions.OnlyOnCanceled);  

Console.WriteLine("Press enter to stop the task");                 
Console.ReadLine();                 
cancellationTokenSource.Cancel();                 
task.Wait(); 

Надеюсь, это поможет лучше понять.