2014-12-04 4 views
8

Я пытаюсь получить мою голову вокруг этого кода:Ожидание пустой задачи закручивает навсегда (ждут новые задачи (() => {}))

[TestFixture] 
public class ExampleTest 
{ 
    [Test] 
    public void Example() 
    { 
     AwaitEmptyTask().Wait(); 
    } 

    public async Task AwaitEmptyTask() 
    { 
     await new Task(() => { }); 
    } 
} 

Метод Example никогда не заканчивается и блоки навсегда. Почему?

затруднительное (от Stubbing Task returning method in async unit test), чтобы заменить await new Task(() => {}) с return Task.FromResult<object>(null);, но опять же, почему это необходимо?

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

+0

Я предлагаю вам использовать 'Task.Factory.StartNew', чтобы не забыть запустить задачу. –

+3

@AlexJoukovsky. Я предлагаю, чтобы он не использовал' Task.Factory.StartNew' и вместо этого использовал 'Task.Run'. [Есть проблемы с использованием StartNew] (http: // blog.stephencleary.com/2013/08/startnew-is-dangerous.html). –

+1

Почему голос? –

ответ

17

Вы создаете задачу и никогда ее не запускаете, поэтому она никогда не заканчивается.

Вы должны использовать Task.Run, чтобы создать задачу, которую вы хотите запустить сразу, вместо использования конструктора Task.

+1

Не запускает ли ключевое слово 'await' задание? –

+1

Он прикрепляет продолжение к задаче. Больше ничего. Из собственного кода вы можете сказать, что он явно * не запускает задачу. Если да, то ваш код действительно завершится. – Servy

+1

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

9

Вам необходимо позвонить по телефону Start() по вашей задаче.

В противном случае он никогда не закончится.

+1

Не запускает ли ключевое слово 'await' задание? –

+1

@PhilipPittle нет, он только ждет завершения задачи. –

+1

@SLaks - Мог ли я беспокоить вас, чтобы обновить это. Где именно я бы назвал 'Start()'? В 'Example()' или 'AwaitEmptyTask()'? Вызов 'AwaitEmptyTask(). Start();' in 'Example()' throws 'System.InvalidOperationException: запуск не может быть вызван задачей в стиле обеда.' –

4

Даже если вы изменили его

await Task.Run(() => { }); 

может еще никогда не полной. Когда вы нажимаете эту строку, программа создает задачу продолжения, которая будет продолжена, когда задача завершится. Тот факт, что задача пуста, не имеет значения. В этот момент поток управления возвращается к

AwaitEmptyTask().Wait(); 

, который ждет задача, чтобы закончить и блокирует текущий поток.

Когда задача завершена, она пытается продолжить текущий поток. Однако он не может, потому что поток заблокирован вызовом Wait().

Измените долгожданную строку

await Task.Run(() => { }).ConfigureAwait(false); 

Это общая проблема запирания. Не смешивайте Wait() и ждите. См. http://msdn.microsoft.com/en-us/magazine/jj991977.aspx

+0

Это предполагает, что существует один поток синхронизации синхронизации. Это * возможно * не здесь, но это действительно плохой дизайн именно по этой причине. – Servy

+0

@Servy - Я не уверен, что я последую за тобой. Не могли бы вы уточнить? –

+0

Если 'SynchronizationContext.Current' пуст или контекст синхронизации по умолчанию, то тот факт, что вызывающий абонент ждет, не препятствует продолжению продолжения. Ему нужно будет вызвать «Пример» из, скажем, потока пользовательского интерфейса настольного приложения с контуром сообщения, чтобы это произошло. Являясь тестовым методом, это, вероятно, не так; это скорее всего консольное приложение без контекста синхронизации (или по умолчанию). – Servy

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