2015-02-19 6 views
2

Видимо я не понял async/await еще и следующий простой пример, уже вызывает некоторую головную боль:async/await - я использую неправильный контекст синхронизации?

Для тестирования я создать окно, которое представляет свой пользовательский интерфейс для теперь и я хочу сгореть асинхронный метод, который делает некоторую работу в фон при открытии окна. Я добавил Listview в мое окно, чтобы проверить, отвечает ли пользовательский интерфейс.

Когда я выполнить следующий код, две вещи, которые я не понимаю:

  1. Мои ListView показывает CustomObjects, что я определить в конструкторе. Поскольку я не мог использовать ключевое слово await в моем конструкторе, я ожидал, что вызов GetWebResponseAsync().ConfigureAwait(false) заблокирует мой пользовательский интерфейс и создаст тупик (например, заменяет вызов GetWebResponseAsync().Wait()). Кажется, что ConfigureAwait(false) уже делает мой метод запущенным в другом потоке, хотя я не запускал его как задачу или не ожидал его?
  2. Пользовательский интерфейс отображается, но зависает при запуске метода Async. Это на самом деле не неожиданно, но меня смущает, если я рассматриваю предыдущее замечание о том, что Listview, по-видимому, доступен из кода, когда я выполняю свой метод async. Разве это не означает, что мой UI-поток НЕ заблокирован, и поэтому я должен иметь возможность взаимодействовать с пользовательским интерфейсом?

С тех пор как я застрял здесь, мне также хотелось бы знать, как я могу правильно вызвать мой метод асинхронного анализа в конструкторе сразу. Если я использую await GetWebResponseAsync().ConfigureAwait(false);, он не компилируется, так как у моего конструктора нет ключевого слова async. Однако я знаю, что я не должен использовать async void (и даже если я попытаюсь сделать мой конструктор async void, он не скомпилируется, потому что member names can not be the same as their enclosing type).

public partial class TitleWindow : Window 
{ 
    public TitleWindow() 
    { 
     InitializeComponent(); 

     // I can not use this because the constructor is not async. 
     //Task tempTask = await GetWebResponseAsync().ConfigureAwait(false); 

     GetWebResponseAsync().ConfigureAwait(false); 

     //This should in theory test if my UI-Thread is blocked?! 
     List<CustomObject> items = new List<CustomObject>(); 
     items.Add(new CustomObject() { Title = "CustomTitle", Year = 2100}); 
     items.Add(new CustomObject() { Title = "CustomTitle2", Year = 2015}); 
     lvTitles.ItemsSource = items; 

    } 

    public async Task GetWebResponseAsync(){ 
     WebRequest request = WebRequest.Create("http://www.google.com"); 
     request.Credentials = CredentialCache.DefaultCredentials; 
     WebResponse response = await request.GetResponseAsync(); 

     //Output for test purposes. 
     Stream dataStream = response.GetResponseStream(); 
     StreamReader reader = new StreamReader(dataStream); 
     string responseFromServer = await reader.ReadToEndAsync(); 
     Console.WriteLine(responseFromServer); 
     return; 
    } 
} 

Update:

Юваль Itzchakovs Ответ отлично работает для меня.

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

public partial class TitleWindow : Window, IAsyncInitialization 
{ 
    public Task Initialization{get; private set;} 

    public TitleWindow() 
    { 
     InitializeComponent(); 
     Initialization = GetWebResponseAsync(); 
    } 

    public async Task GetWebResponseAsync(){ 
      //unchanged 
    } 

ответ

6

Похоже ConfigureAwait (ложь) уже делает мой метод работы на другой нити, даже если я не запустить его в качестве задачи или ждать его?

Это неправильная информация. Метод будет работать синхронно, пока не будет достигнут первый ключевой код await. После этого, так как вы не используете ConfigureAwait(false) в GetWebResponseAsync, он будет маршалировать его продолжение снова на поток пользовательского интерфейса, что, вероятно, является причиной того, что вы видите, что вы UI застреваете.

Не означает, что мой UI-поток НЕ заблокирован, и поэтому я должен быть в состоянии взаимодействовать с пользовательским интерфейсом?

Опять же, нет.Асинхронный метод не работает в фоновом потоке, он потребляется в потоке пользовательского интерфейса. Когда метод запускает свою синхронную часть (например, Stream dataStream = response.GetResponseStream();), она все еще выполняется в потоке пользовательского интерфейса.

Вызов метода async из конструктора, естественно, не работает, потому что конструкторы не являются асинхронными (Stephan Cleary имеет хороший blog post).

Что вы могу сделать использование прикрепляется к событию, которое выстреливает после загрузки окна, таких как Loaded события, которое вы можете выполнить методы асинхронных внутри правильно:

this.Loaded += OnWindowLoaded; 

И тогда вы может правильно await:

private async void OnWindowLoaded(object sender, RoutedEventArgs e) 
{ 
    await GetWebResponseAsync().ConfigureAwait(false); 
} 

Обратите внимание, что если нет необходимости в контексте синхронизации внутри GetWebResponseAsync, вы можете также использовать ConfigureAwait(false) и са вы сами накладные расходы на синхронизацию контекста.

+0

классный, это работает как шарм :) Думаю, я также понял ваши очень четкие объяснения. Спасибо! –

+0

Да. Фактически он работал на вашем потоке пользовательского интерфейса. Вы можете проверить это, напечатав «ManagedThreadId» перед печатью ответа. Это происходит потому, что вы не используете 'ConfigureAwait (false)' внутри вашего метода веб-запроса. –

+0

У меня есть еще один вопрос: ссылка, которую вы указали, имеет главу «Что НЕ делать» в самом конце. Не подключает мой асинхронный метод к событию загрузки, как вы предполагали, имеет те же недостатки, что и пример в этой главе статьи в блоге Стивена Клириса? В моем минималистическом примере ваше предложение работает отлично, конечно, так как мне не нужно знать, когда выполняется асинхронный метод, инициализирующий мой объект. Однако после прочтения сообщения в блоге я думаю, что я не должен использовать это вообще, если только я не предоставляю внутреннюю обработку ошибок и не может гарантировать, что объект уже правильно инициализирован? –

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