2014-09-27 6 views
4

У меня есть теоретический вопрос. Что произойдет, если я ожидаю результат задачи в другой задаче? Я хочу знать, будет ли моя текущая система работать позже.Что происходит, когда вы ждете неудавшейся задачи

Задача запускается и делает некоторые вещи. В какой-то момент эта задача может понадобиться другой для обработки данных, которые текущая задача не может обрабатывать сама по себе. Поэтому я жду, чтобы убедиться, что текущая задача не будет продолжаться до тех пор, пока у него нет результата вспомогательной задачи. Но что произойдет, если хелпер не сработает? Будет ли текущая задача оставаться заблокированной?

Могу ли я избежать этого тупика каким-то образом (без изменения самой системы - задачи внутри задачи)?

+3

Как насчет написания простого тестового кода и просмотра результата? –

+0

Вы не можете «ждать» 'Result', вы можете подождать задание. Если он провалился, он выкинет исключение –

+0

Конечно, я мог бы написать тестовый код, но это займет гораздо больше времени, чем спрашивать здесь. И это не должно быть слишком много усилий, чтобы ответить на это :) В противном случае мне очень жаль. Извините, моя вина. Конечно, я имел в виду ждать задания. Итак, теоретически «ожидание» выдает исключение, если ожидаемая задача не выполняется? – SharpShade

ответ

4

Задача запускается и выполняет некоторые действия. В какой-то момент эта задача может понадобиться другой для обработки данных, которые текущая задача не может обрабатывать сама по себе. Поэтому я жду, чтобы убедиться, что текущая задача не будет продолжаться до тех пор, пока у него нет результата вспомогательной задачи. Но что произойдет, если хелпер не сработает? Будет ли текущая задача оставаться заблокированной?

Основная идея позади async и await является то, что асинхронный код работает примерно так же, как синхронный код.

Так что, если у вас есть синхронное такой код:

void HelperMethod() 
{ 
    throw new InvalidOperationException("test"); 
} 

void DoStuff() 
{ 
    HelperMethod(); 
} 

тогда можно было бы ожидать DoStuff распространять InvalidOperationException от вспомогательного метода. Кроме того, это то, что происходит с асинхронным кодом:

async Task HelperMethodAsync() 
{ 
    throw new InvalidOperationException("test"); 
} 

async Task DoStuffAsync() 
{ 
    await HelperMethodAsync(); 
} 

То есть, DoStuffAsync также будет распространяться на InvalidOperationException.

Теперь он не работает точно точно так же, конечно, так как он должен быть асинхронными, но общая идея состоит в том, что все ваши управления потоком, такие как try/catch, for петли и т.д., все " просто работать "для асинхронного кода очень похоже на синхронный код.

Что фактически происходит в том, что, когда HelperMethod заканчивается InvalidOperationException, исключение перехватывается и помещен на возвращаемом Task, и задача выполнена. Когда await в DoStuffAsync видит, что задача завершена, она рассматривает свои исключения и повторно поднимает первый (в этом случае есть только один, InvalidOperationException). И он повторно повышает его таким образом, который сохраняет стек вызовов в исключении. Это, в свою очередь, приводит к тому, что Task, возвращенный с DoStuffAsync, должен быть завершен с тем же исключением.

Таким образом, под одеялом async и await делают немного работы, чтобы убедиться, что вы можете просто вызывать другие методы с await и использовать try/catch точно так же, как если бы в синхронном коде. Но большую часть времени вам не нужно об этом знать.

+0

Спасибо за хорошее объяснение. Другой вопрос, связанный с этим: когда я вызываю «task.Result» (или иначе task.Wait()), тогда он действительно блокируется, не так ли? Является ли это возможным источником Deadlock, или это будет безопасно? – SharpShade

+1

Я считаю, что вы имеете в виду [тупиковый сценарий, который я описываю в своем блоге] (http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html). –

+0

Какое совпадение. У меня действительно был только этот сценарий, и это была даже не проблема, с которой я сначала беспокоился. Но все же очень полезно, потому что этот сценарий происходит сейчас именно в той позиции, в которой я думал, что может: Мой уже асинхронный запущенный метод (сам метод не async, но он работает во втором потоке) создает другую задачу и должен дождаться ее завершения. Такая же проблема: -/Мне действительно нужно перепроектировать всю мою систему, чтобы иметь асинхронные методы, которые могут ждать этих задач ... – SharpShade

1

Это действительно легко проверить. Например:

[TestMethod, ExpectedException(typeof(Exception))] 
    public async Task DoFaultedTaskThrowOnAwait() 
    { 
     var task = Task.Factory.StartNew(() => { throw new Exception("Error 42"); }); 
     await task; 
    } 
    [TestMethod, ExpectedException(typeof(AggregateException))] 
    public void DoFaultedTaskThrowOnWait() 
    { 
     var task = Task.Factory.StartNew(() => { throw new Exception("Error 42"); }); 
     task.Wait(); 
    } 

Оба испытания проходят, обратите внимание, что Wait бросает AggregateException и await бросает Exception.

+0

Хорошо, ты прав. Но я еще не использовал Testing Framework. Я не знал, что это было так просто ... – SharpShade

2

Что произойдет, если я жду результата задачи в другой задаче?

Вы только awaitTask.Result если это awaitable может (то есть он имеет метод GetAwaiter). Это редко бывает. Я предполагаю, что вы имеете в виду await по внутренней задаче.

Но что произойдет, если хелпер не сработает? Будет ли текущая задача оставаться заблокированной?

Во-первых, им не понятно, что вы подразумеваете под «заблокированным». Задача не заблокирована во время выполнения задачи await. Контроль возвращается к вызывающему методу до тех пор, пока эта внутренняя задача не завершится. Если эта внутренняя задача выходит из строя, и вы не можете должным образом обработать это исключение, ваша родительская задача также будет ошибочной. Вы должны убедиться, что вы обрабатывать исключения изящно:

var task = 
    Task.Run(async() => 
      { 
       try 
       { 
         await AsyncHelper.DoSomethingAsync(); 
       } 
       catch (Exception e) 
       { 
         // Handle exception gracefully 
       } 
      }); 

Если вы await на родительской задачи, вы заметите внутреннее исключение propogating из необработанной внутренней задачи.

Могу ли я избежать этого тупика?

Я не вижу причин, по которым ваш код должен быть заблокирован в этом конкретном случае. Не знаете, почему это вас беспокоит.

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