Есть ли способ вызвать немедленную остановку работающего метода с помощью cts.Cancel ();

У меня есть код, который создает CancellationTokenSource и передает его методу.

У меня есть код в другом приложении, которое выдает cts.Cancel ();

Есть ли способ вызвать немедленную остановку этого метода, не дожидаясь завершения двух строк внутри цикла while?

Обратите внимание, что я был бы в порядке, если бы это вызвало исключение, которое я мог бы обработать.

public async Task OnAppearing()
{
   cts = new CancellationTokenSource();
   await GetCards(cts.Token);
}

public async Task GetCards(CancellationToken ct)
{
   while (!ct.IsCancellationRequested)
   {
      App.viewablePhrases = App.DB.GetViewablePhrases(Settings.Mode, Settings.Pts);
      await CheckAvailability();
   }
}

person Alan2    schedule 09.12.2019    source источник
comment
Отмена возможна. более старой моделью, которую он заменил, были потоки и thread.abort, которые делают то, о чем вы просите, но их настолько сложно использовать правильно (в общем случае), что от них в основном отказались.   -  person Damien_The_Unbeliever    schedule 09.12.2019
comment
@Damien_The_Unbeliever Thread.Abort не реализован в .NET Core. Но да, это будет работать в .NET Framework.   -  person AngryHacker    schedule 09.12.2019
comment
Связано: Как преобразовать task.Wait (CancellationToken) в ожидание заявление?   -  person Theodor Zoulias    schedule 16.12.2020


Ответы (2)


Что могу посоветовать:

  1. Измените GetViewablePhrases и CheckAvailability, чтобы вы могли передать им CancellationToken;
  2. Используйте ct.ThrowIfCancellationRequested () внутри этих функций;
  3. Попробуйте / перехватить OperationCanceledException внутри GetCards;

Что касается ваших функций, я не знаю, как именно они работают внутри. Но предположим, что у вас есть длительная итерация внутри одного из них:

CheckAvailability(CancellationToken ct)
{
    for(;;) 
    {
        // if cts.Cancel() was executed - this method throws the OperationCanceledException
        // if it wasn't the method does nothing
        ct.ThrowIfCancellationRequested(); 
        ...calculations... 
    } 
}

Или предположим, что вы собираетесь получить доступ к своей базе данных внутри одной из функций, и вы знаете, что этот процесс займет некоторое время:

CheckAvailability(CancellationToken ct)
{
    ct.ThrowIfCancellationRequested();
    AccessingDatabase();
}

Это не только предотвратит продолжение выполнения ваших функций, но и установит статус задачи исполнителя как TaskStatus.Canceled

И не забудьте уловить Exception:

public async Task GetCards(CancellationToken ct)
{
   try
   {
      App.viewablePhrases = App.DB.GetViewablePhrases(Settings.Mode, Settings.Pts, ct);
      await CheckAvailability(ct);
   }
   catch(OperationCanceledException ex)
   {
       // handle the cancelation...
   }
   catch
   {
       // handle the unexpected exception
   }
}
person Gleb    schedule 09.12.2019
comment
Не могли бы вы привести пример того, как я бы использовал ct.ThrowIfCancellationRequested внутри функции. Очень простой пример может помочь. - person Alan2; 09.12.2019
comment
@ Alan2 Я немного изменил свой ответ для вас. Я надеюсь, что это помогает. - person Gleb; 09.12.2019

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

public static Task AsCancelable(this Task task,
    CancellationToken cancellationToken)
{
    var cancelable = new Task(() => { }, cancellationToken);
    return Task.WhenAny(task, cancelable).Unwrap();
}

public static Task<T> AsCancelable<T>(this Task<T> task,
    CancellationToken cancellationToken)
{
    var cancelable = new Task<T>(() => default, cancellationToken);
    return Task.WhenAny(task, cancelable).Unwrap();
}

Пример использования:

await GetCards(cts.Token).AsCancelable(cts.Token);

Этот метод расширения также можно реализовать с помощью TaskCompletionSource<T> (вместо конструктора Task<T>).

person Theodor Zoulias    schedule 10.12.2019