C # Как обработать задачу отмены с помощью обработчика событий внутри - PullRequest
0 голосов
/ 17 мая 2018

У меня есть требование обновить пользовательский интерфейс, когда изменится статус зависимой службы.У меня есть этот пример кода, который запрашивает API службы для получения статуса и отправляет результат для пересчета и обновления пользовательского интерфейса по основному потоку:

public void StartObserving() {
this.cts = new CancellationTokenSource();
this.cts.Token.ThrowIfCancellationRequested();
this.isRunning = true;
var token = this.cts.Token;

Task.Run(async () =>
{
    try
    {
        while (this.isRunning)
        {
            var result = this.serviceAPI.GetStatus();
            this.OnServiceStatusChanged(result);

            await Task.Delay(3000);
        }
    }
    catch (OperationCanceledException)
    {
        this.isRunning = false;
    }
    catch (Exception ex)
    {
        this.isRunning = false;
        this.logger.LogError(ex);
    }
}, token);
}

И проблема в том, когда я хочу отменить выше задачи.Когда я вызываю this.cts.Cancel () в другом методе этого класса, я получаю исключение «Задача была отменена» в диспетчере, которое было вызвано EventHandler: OnServiceStatusChanged

Как правильно реализовать этот сценарий?

Ответы [ 3 ]

0 голосов
/ 17 мая 2018

Пытался смоделировать это поведение в консольном приложении.Задача запущена, но после вызова cts.Cancel () задача продолжает выполняться ... Очень странно.

Однако я мог бы отменить задачу, просто установив this.isRunning в false (вместо вызова cts.Отменить()).Но я не уверен, что это то решение, которое вам нужно.

0 голосов
/ 17 мая 2018

Если serviceAPI.GetStatus() является блокирующим вызовом, который ждет бесконечно, то вы не можете правильно отменить эту задачу.

Правильная отмена методов async предполагает пометку безопасных точек отмены CancellationToken.ThrowIfCancellationRequested().

Вам придется переписать serviceAPI.GetStatus() как async метод, результатом которого вы await.Он должен содержать вызовы CancellationToken.ThrowIfCancellationRequested() в точках, где его можно безопасно отменить.Вы хотели бы передать токен отмены в и этого метода, и вызов Task.Delay() для оптимальной производительности.

0 голосов
/ 17 мая 2018

Я бы просто проверил, отменен ли токен во внутреннем цикле, и вышел бы из цикла, если он есть. Нет необходимости передавать токен методу Task.Run ().

public void StartObserving()
{
    this.cts = new CancellationTokenSource();
    var token = this.cts.Token;

    Task.Run(async () =>
    {
        try
        {
            while (!token.IsCancellationRequested)
            {
                var result = this.serviceAPI.GetStatus();
                this.OnServiceStatusChanged(result);
                await Task.Delay(3000);
            }
        }
        catch
        {
        }
    });
}
...