2017-02-11 1 views
2

Итак, вот чего я пытаюсь достичь. Я запускаю задачу и не буду ждать/результат. Чтобы убедиться, что если запущенная задача переходит в состояние сбоя (например, скажем, исключение), я разбиваю процесс, вызывая Environment FailFast в продолжении.Задача не переходит в состояние с ошибкой, если для параметра ConfigurAwait установлено значение False

Как проблема, с которой я сталкиваюсь, заключается в том, что если я побежал ниже кода, Inside ContinueWith, статус задачи (которая забросила исключение) появляется как «RanToCompletion». Я ожидал, что это будет Faulted State.

private Task KickOfTaskWorkAsync() 
    { 
     var createdTask = Task.Run(() => this.RunTestTaskAsync(CancellationToken.None).ConfigureAwait(false), CancellationToken.None); 

     createdTask.ContinueWith(
      task => Console.WriteLine("Task State In Continue with => {0}", task.Status)); 

     return createdTask; 
    } 

    private async Task RunTestTaskAsync(CancellationToken cancellationToken) 
    { 
     throw new Exception("CrashingRoutine: Crashing by Design"); 
    } 

Это действительно странно :(Если я удалю «ConfigureAwait (ложь)» внутри вызова функции Task.Run, задача действительно переходит в состояние FAULTED внутри Продолжить. Действительно в убыток, чтобы объяснить, что происходит и был бы признателен за помощь со стороны сообщества.

[Обновить]: Мой коллега указал на явную ошибку. Я использую ConfigureAwait, когда я звоню в RunTestAsync внутри Test.Run, хотя я этого не жду. , ConfigureAwait не возвращает задачу в Task.Run.Если я не вызываю ConfigureAwait, задача возвращается, и все работает должным образом.

+1

Есть ли причина, по которой вы используете 'ContinueWith'? 'ContinueWith' был для пред-асинхронной эры; теперь вы можете просто использовать 'await RunTestTaskAsync()', за которым следует 'Console.WriteLine()' и поймать любые исключения, которые вы хотите более стандартным образом; т. е. 'try'..'catch'. – sellotape

+0

@sellotape: правильно, что 'await' - более идиоматический способ справиться с продолжениями сейчас. Однако OP по-прежнему будет иметь такую ​​же проблему, даже используя 'await'. Они будут ожидать неправильный объект «Задача» и все равно будут видеть задачу в состоянии без сбоев. –

+0

@PeterDuniho - если бы он ожидал 'createdTask', то да, но если он просто ждет' RunTestTaskAsync() '(что, по-видимому, мало причин, но я думаю, что недостаточно контекста, чтобы убедиться)' catch' поймает исключение, которое он бросает. – sellotape

ответ

-2

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

В вашем примере кода RunTestTaskAsync() возвращает объект Task. Он завершается синхронно (потому что нет await), поэтому объект Task, который он возвращает, уже сбой, когда метод возвращает из-за исключения. Затем ваш код вызывает ConfigureAwait() об этом неисправном объекте Task.

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

Причина, по которой вы наблюдаете исключение при удалении вызова ConfigureAwait(), не имеет никакого отношения к самому вызову. Если вы оставите вызов и отправили true, вы все равно не заметите исключение. Причина, по которой вы можете наблюдать за исключением, когда вы удаляете вызов, заключается в том, что без вызова ConfigureAwait() возвращаемое значение выражения лямбда составляет Task, и это вызывает a different overload of Task.Run().

Эта перегрузка немного отличается от других. Из документации:

Очередь заданной работы для запуска в пуле потоков и возвращает прокси-сервер для задачи, возвращаемой функцией.

То есть, в то время как она по-прежнему начинается новый Task, то Task объект возвращает представляет собой не что Task, но один возвращенный лямбда-выражения. И этот прокси принимает такое же состояние, как и Task, которое оно обертывает, поэтому вы видите его в состоянии Faulted.

Основываясь на коде, который вы опубликовали, я бы сказал, что вы не должны звонить Task.Run() в первую очередь.Ниже будет работать так же хорошо, без накладных расходов и сложности прокси:

static void Main(string[] args) 
{ 
    Task createdTask = RunTestTaskAsync(); 

    createdTask.ConfigureAwait(false); 

    createdTask.ContinueWith(
     task => Console.WriteLine("Task State In Continue with => {0}", task.Status)).Wait(); 
} 

private static async Task RunTestTaskAsync() 
{ 
    throw new Exception("CrashingRoutine: Crashing by Design"); 
} 

(я удалил CancellationToken значения, потому что они не имеют ничего общего с вашим вопросом и совершенно лишними здесь.)

+0

Какой смысл настраивать 'await' (т. Е.' CreatedTask.ConfigureAwait (false) ') без соответствующего' await'? И, глядя более внимательно, кажется, что этот код никогда не запускается (перед этим будет выбрано исключение). –

+0

@ ta.speot.is: на продолжение влияет вызов 'ConfigureAwait()', выполняемый с помощью 'ContinueWith()' или оператора 'await'. И вы также ошибаетесь в отношении исключения. Это не наблюдается в потоке, потому что он инкапсулирован «Задачей», возвращаемой методом. Если бы вы потрудились запускать код, вы бы это знали. Забавно, как люди голодают, не понимая, на что они смотрят. –

+0

RE: # 1 У меня под впечатлением 'ConfigureAwait' возвращает' ConfiguredTaskAwaitable' и * это * то, что содержит конфигурацию. Вы правы RE: # 2. –

Смежные вопросы