2014-09-18 3 views
0

Какая разница между:Ожидание фонового потока в C#

public class Worker 
{ 
    public static void Execute() 
    { 
     ExecuteInBackgroundThread().Wait(); 
    } 

    private static Task ExecuteInBackgroundThread() 
    { 
     return Task.Run(() => 
     { 
      // do some long running operation 
     }); 
    } 
} 

и

public class Worker 
{ 
    public static void Execute() 
    { 
     ExecuteInBackgroundThread().Wait(); 
    } 

    private static async Task ExecuteInBackgroundThread() 
    { 
     await Task.Run(() => 
     { 
      // do some long running operation 
     }); 
    } 
} 

Я заметил, что, называя вторую версию Worker.Execute() из пользовательского интерфейса нитку мой телефон приложение для Windows застревает.

Вместо этого, используя первую версию, все работает нормально.

Является ли вторая версия ExecuteInBackgroundThread фактическим возвратом Task<T>, что можно ожидать? Но если это не так, должен ли компилятор не давать ошибку, говоря, что мы ничего не возвращаем?

ответ

2

Давайте посмотрим, что вызов Execute для каждой версии делает:

Первая версия называет ExecuteInBackgroundThread которая сразу возвращающую Task. Затем Execute блокирует, пока возвращенная задача не будет завершена.

Вторая версия вызывает ExecuteInBackgroundThread, которая создает Task и планирует продолжение, которое будет выполняться при завершении задачи и возвращает другую задачу. Затем Execute блокирует, пока возвращенная задача не будет завершена.

Разница в том, что вторая версия имеет запланированное продолжение из-за ключевого слова ожидания. Эффект от этого зависит от контекста синхронизации потока, в котором вы вызываете Execute. Когда вы находитесь в потоке пользовательского интерфейса, вторая версия будет блокирована, потому что продолжение запланировано в потоке пользовательского интерфейса, который заблокирован из-за вызова Wait.

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

+0

Спасибо, я действительно заметил, что не было тупика, запускающего его из тестового бегуна. –

5

Прежде всего, настоящая причина, по которой ваше приложение застревает в том, что у вас есть тупик. Это потому, что вы используете Task.Wait, который блокирует поток пользовательского интерфейса для задачи, ожидающей завершения потока пользовательского интерфейса.

Это происходит потому, что существует только один поток пользовательского интерфейса, поэтому есть SynchronizationContext, который использует только этот конкретный поток.

Вы можете исправить затор с помощью ConfigureAwait(false), который не использует SynchronizationContext:

private static async Task<T> ExecuteInBackgroundThread<T>() 
{ 
    await Task.Run(() => 
    { 
     // do some long running operation 
    }).ConfigureAwait(false); 
} 

Кроме того, оба варианта почти так же, до тех пор, пока больше нет кода в ExecuteInBackgroundThread который вне звонка до Task.Run. Первый вариант немного быстрее, потому что вы используете задачу из Task.Run напрямую. Второй вариант добавляет еще один избыточный асинхронный слой (включая конечный автомат и т. Д.) Сверху.

Вывод: вы должны использовать первый вариант. Но не используйте Wait, который не только блокирует ваши потоки, но может привести к блокировке в некоторых случаях.

+0

Причина, по которой я использую 'Wait()', заключается в том, что это выполняется только один раз при запуске приложения и вызывается из 'App.xaml.cs'. Это инициализирует базу данных и должно быть выполнено до того, как будет выполнено другое. –

+1

@LorenzoPolidori Нет ли приложения Application.Load? просто поместите этот код в самый ранний обработчик событий и сделайте его асинхронным. – i3arnon

+0

Хорошо, я попробую. –

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