C# async CTP. Как пометить асинхронную задачу как отмененную, не создавая исключение TaskCanceledException?

У меня есть короткая асинхронная задача, которую часто нужно отменять после ее запуска. Класс «Task» имеет индикатор IsCanceled, который, я думаю, было бы удобно использовать, чтобы указать, что асинхронная задача была отменена без выполнения до завершения, но, насколько я могу судить, единственный способ пометить асинхронную задачу как отмененную чтобы вызвать исключение TaskCanceledException в асинхронной функции. Регулярное выбрасывание исключения, чтобы указать на ситуацию, которая возникает без исключения, противоречит тому, как я понимаю, что следует использовать исключения. Кто-нибудь знает лучший способ указать, что асинхронная задача должна быть отменена, когда ожидается, что она будет происходить часто?

Моя следующая лучшая альтернатива — вернуть структуру, имеющую собственное свойство IsCanceled:

(Для краткости я проигнорировал некоторые хорошие методы кодирования и стиля)

class MightBeCanceled<T>
{
    public readonly T Value;
    public readonly bool IsCanceled;

    public MightBeCanceled(T value) { Value = value; IsCanceled = false; }
    public static MightBeCanceled<T> Canceled = new MightBeCanceled<T>(default(T), true);
    private MightBeCanceled(T value, bool isCanceled) { Value = value; IsCanceled = isCanceled; }
}

...

static async Task<MightBeCanceled<int>> Foo()
{
    if (someCancellationCondition)
        return MightBeCanceled<int>.Canceled;
    else
        return new MightBeCanceled<int>(42);
}

static async void Bar() 
{
    var mightBeCanceled = await Foo();

    if (mightBeCanceled.IsCanceled)
        ; // Take canceled action
    else
        ; // Take normal action
}

Но это кажется избыточным и более сложным в использовании. Не говоря уже о том, что это создает проблемы согласованности, потому что будет два IsCanceled (один в Task и один в MightBeCanceled).


person Mike    schedule 11.08.2011    source источник
comment
Кто отменяет Фу? Он должен быть отменен только вызывающим абонентом с использованием CancellationToken.   -  person Arne Claassen    schedule 22.08.2011


Ответы (2)


Обычно цель Cancellation состоит в том, чтобы инициатор асинхронного действия сообщил действию, что оно должно прекратить обработку. Для этого вы передаете CancellationToken в асинхронный метод. Вот почему ожидание задачи, которая устанавливает IsCancelled, сбрасывает себя. Это означало исключительный сигнал к действию, спровоцированному извне. Отмена задачи не должна использоваться для управления потоком, а только для того, чтобы дать асинхронному действию возможность закончить досрочно, если ожидающая сторона сообщила, что ей больше не нужен результат.

Если ваши асинхронные действия отменяются внутри, вы уже перегрузили концепцию, и я бы сказал, что то, что отражает эту разницу в отмене, имеет смысл и должно быть свойством контейнера результатов, аналогичным тому, как вы его предложили. Но вместо того, чтобы называть его MightBeCancelled<T>, может быть, что-то вроде InternallyCancellableResult<T>, чтобы отразить разницу в концепциях отмены.

person Arne Claassen    schedule 22.08.2011

Проблема в том, что исключение не будет наблюдаться, пока вы не решите подождать его. Таким образом, вызов await означает, что вы в конечном итоге обнаружите исключение в какой-то момент, когда будет возвращено окончательное значение или если вы дождетесь завершения задачи. Однако, если вам все равно (это задача «сделай и забудь»), то исключение не является проблемой (по большей части).

Я тоже нахожу это немного странным, всегда приходится пытаться/ловить задачу для обработки агрегированных исключений. Тем не менее, подумайте об этом немного:

 try
 {
     var myResult = await Foo();

     // Do Success Actions Here... 
 }
 catch(AggregateException e)
 {
     e.Flatten().Handle(ex =>
       {
           if(ex is OperationCanceledException)
           {
               // Do Canceled Thing Here
               return true;
           }

           return false;
       });
 }

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

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

person Tejs    schedule 11.08.2011