2013-06-22 2 views
10

Я читал об асинхронных вызовах функций на http://msdn.microsoft.com/en-us/library/vstudio/hh191443.aspx?cs-save-lang=1&cs-lang=csharp.Как работает асинхронный вызов/ждут от синхронного вызова?

В первом примере, они делают это, что я получаю:

Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com"); 

// You can do work here that doesn't rely on the string from GetStringAsync. 
DoIndependentWork(); 

string urlContents = await getStringTask; 

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

string urlContents = await client.GetStringAsync(); 

Из того, что я понимаю, await ключевое слово будет приостанавливать поток кода до возврата. Итак, как это отличается от:

string urlContents = client.GetString(); 

?

+0

Он не блокирует основной контекст выполнения/поток, так, например, Пользовательский интерфейс не будет заморожен (в приложении, использующем интерфейс). –

+0

Но это в основном потому, что сам метод является 'async', правильно? Разве это не означает, что до тех пор, пока он занят вещами, вызывающий поток может продолжаться? – Rijk

+0

Нет, требуется асинхронный вызов, чтобы он мог вызвать ожидание. Вся магия происходит в ожидании. – ghord

ответ

8

Вызов await client.GetStringAsync() дает выполнение вызова вызывающему методу, что означает, что он не будет ждать завершения выполнения метода и, следовательно, не будет блокировать поток. Как только это будет выполнено в фоновом режиме, метод продолжит работу с того места, где он остановился.

Если вы просто вызываете client.GetString(), выполнение потока не будет продолжаться до тех пор, пока этот метод не завершит выполнение, что заблокирует поток и может привести к тому, что пользовательский интерфейс перестанет отвечать на запросы.

Пример:

public void MainFunc() 
{ 
    InnerFunc(); 
    Console.WriteLine("InnerFunc finished"); 
} 

public void InnerFunc() 
{ 
    // This causes InnerFunc to return execution to MainFunc, 
    // which will display "InnerFunc finished" immediately. 
    string urlContents = await client.GetStringAsync(); 

    // Do stuff with urlContents 
} 

public void InnerFunc() 
{ 
    // "InnerFunc finished" will only be displayed when InnerFunc returns, 
    // which may take a while since GetString is a costly call. 
    string urlContents = client.GetString(); 

    // Do stuff with urlContents 
} 
+2

Я, наконец, получил его, когда прочитал «Разница между синхронным и асинхронным поведением - синхронный метод возвращается, когда его работа завершена, но метод async _ возвращает значение задачи, когда его работа приостановлена». Таким образом, асинхронная функция будет по-прежнему вести себя как синхронные функции, * до тех пор, пока они не выдадут оператор 'await', который вернет управление вызывающему. – Rijk

+1

@Rijk: асинхронная функция не обязательно помечена идентификатором 'async' - и не обязательно выдает' await'. Нормальная функция, возвращающая «Задача» или «Задача », может рассматриваться как асинхронная функция, и вы можете «ждать» ее. – YK1

8

Из того, что я понимаю, Await ключевое слово будет приостановить поток кода, пока функция не возвращает

Ну, да и №

  • Да, потому что поток кода в каком-то смысле останавливается.
  • Нет, потому что поток, выполняющий этот поток кода, не блокируется. (Синхронный вызов client.GetString() заблокирует поток).

Фактически, он вернется к своему методу вызова. Чтобы понять, что это значит, вернувшись к его вызывающему методу, вы можете прочитать о другом мастере компилятора C# - в заявлении yield return.

итераторы блоки с yield return сломаются метод в состоянии машины - где код после yield return заявления будет выполнять только после того, как MoveNext() вызывается счетчику. (См. this и this).

Теперь, async/await механизм также основан на аналогичной машине состояния (однако, ее гораздо сложнее, чем yield return автомат).

Для упрощения, давайте рассмотрим простой метод асинхронной:

public async Task MyMethodAsync() 
{ 
    // code block 1 - code before await 

    // await stateement 
    var r = await SomeAwaitableMethodAsync(); 

    // code block 2 - code after await 
} 
  • Когда вы отмечаете метод с async идентификатором компилятору разбить метод в государственную машину, и что вы собираетесь await внутри этот способ.
  • Скажем, код работает по потоку Thread1, и ваш код называет это MyMethodAsync(). Затем code block 1 будет синхронно работать в одном потоке.
  • SomeAwaitableMethodAsync() также будет вызываться синхронно, но позволяет сказать, что метод запускает новую асинхронную операцию и возвращает Task.
  • Это когда await вступает в картину. Он вернет поток кода обратно к вызывающему абоненту, и поток Thread1 может свободно запускать код вызывающих абонентов. То, что происходит тогда в методе вызова, зависит от того, является ли метод вызова await s на MyMethodAsync() или делает что-то еще - но важная вещь Thread1 не заблокирована.
  • Теперь остальное волшебство ожидания - Когда Задача, возвращаемая SomeAwaitableMethodAsync(), заканчивается, code block 2 является Запланировано для запуска.
  • async/await построен на параллельной библиотеке задач - так, это Планирование выполнено по TPL.
  • Теперь дело в том, что этот code block 2 не может быть запланирован на ту же тему: Thread1, если только он не был активным SynchronizationContext с привязкой к потоку (например, WPF/WinForms UI thread). await - SynchronizationContext известно, поэтому code block 2 назначается на тот же SynchronizationContext, если есть, когда был вызван MyMethodAsync(). Если не было активных SynchronizationContext, то при всей возможности code block 2 будет работать над несколькими потоками.

Наконец, я скажу, что с async/await основана на государственной машине, созданной компилятором, как yield return, он разделяет некоторые недостатки - например, вы не можете await Внутри finally блока.

Надеюсь, это прояснит ваши сомнения.

+0

Спасибо за подробный ответ! – Rijk