2014-01-10 3 views
2

Не уверен, что я испорчен своим пониманием того, как работает async, но вот проблема, на которую я застрял. Рассмотрим надуманный примерAsync ждет с Task.FromResult (0)

  1. Этот код блокирует UI

    public async void LoginButtonClicked() 
    { 
    //create a continuation point so every following statement will get executed as ContinueWith 
    await Task.FromResult(0); 
    //this call takes time to execute 
    Remote.Login("user","password"); 
    } 
    
  2. Но это не (очевидно)

    public void LoginButtonClicked() 
    { 
    Task.Run(()=>{ Remote.Login("user","password");}); 
        } 
    

Я хотел бы использовать метод 1, потому что я не Не хочу, чтобы вы отделили длительную работу с помощью Task.Run, но я предпочитаю, чтобы фреймворк обрабатывал эту форму. Но проблема в том, что вызов метода 1 кажется блокирующим.

+1

'await' только делает что-то фантастическое, если дело не направо. 'Task.FromResult' всегда возвращает завершенную задачу, и поэтому метод продолжает весело проезжать мимо этой точки' await'. –

+1

И этот старый Эрик Липперт [сообщение в блоге] (http://blogs.msdn.com/b/ericlippert/archive/2010/10/29/asynchronous-programming-in-c-5-0-part-two-whence -await.aspx) может помочь: «Весь смысл асинхронных методов заключается в том, чтобы вы оставались в текущем потоке как можно больше». –

ответ

4

Использование await/async только останавливает вас от блокировки пользовательского интерфейса, если все длительные операции, которые вы вызываете, являются асинхронными. В вашем примере ваш Remote.Login является синхронным вызовом, поэтому независимо от того, что делает предыдущая строка await, это заблокирует ваш пользовательский интерфейс.

Вы должны либо получить версию асинхронной вашей фактической длительной эксплуатации (например, что-то возвращающее Task) или, если это невозможно, то вы можете прибегнуть к Task.Run, чтобы переместить эту работу в ThreadPool.

Что вы хотите, если это возможно:

public async void LoginButtonClicked() 
{ 
    await Remote.LoginAsync("user","password"); 
    // do anything else required 
} 
-1
  1. Вы бежите в параллельном Task.FromResult (0); и до сих пор ждут Remote.Login («пользователь», «пароль»); готово
  2. Вы запускаете Remote.Login («пользователь», «пароль»); асинхронно.

Вы должны создать версию асинхронной из Remote.Login

async Task LoginAsync(string user, string password) 
    { 
     Remote.Login(user, password); 
     await Task.FromResult(0); 
    } 

и называют его

public async void LoginButtonClicked() 
    { 
     await LoginAsync("user", "password"); 
    } 
+1

Это синхронный метод, а не асинхронный метод. – Servy

1

Каждый метод асинхронной имеет свой контекст.

При запуске задачи он может запускаться в новом SynchronizationContext. «Возможно», потому что если задача уже завершена, например, Task.FromResult(0), тогда никакой другой SynchronizationContext не будет создан и будет использоваться исходный.

Ожидание задания означает, что когда задача будет завершена, следующий оператор будет запущен в исходном SynchronizationContext.

Это поведение можно изменить с помощью Task.ConfigureAwait(continueOnCapturedContext: false). Это означает, что следующий оператор будет продолжен в том же контексте. Но это ничего не изменит, сделав Task.FromResult(0).ConfigureAwait(false), потому что задача уже завершена, и исходный контекст будет использоваться.

Поэтому ваш Remote.Login("user","password"); будет запущен в исходном контексте, тем самым блокируя поток пользовательского интерфейса, который работает в одном контексте.

Если бы что-то вроде:

public async void LoginButtonClicked() 
{ 
    await Task.Delay(5000).ConfigureAwait(false); 
    Remote.Login("user","password"); 
} 

Тогда Remote.Login("user","password"); выполнить бы в таким образом контекст пул потоков будучи на другом контексте, чем исходный контекст пользовательского интерфейса.

Таким образом, лучший способ исправить ваш код - создать Remote.LoginAsync(), как указано в ответе @Nicholas W.

Примечания о производительности:, если у вас есть метод асинхронного с несколькими ждут заявления, и вам не нужны некоторые из этих ожидающих, чтобы сделать работу на UI или веб-приложении потоке, то вы можете использовать Task.ConfigureAwait(false) для того, чтобы предотвратить несколько переключений в контекст пользовательского интерфейса/веб-приложения, который сокращает время выполнения.

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