Использование async await для вычислений ЦП и операций ввода-вывода?

Я уже знаю, что async-await хранит контекст потока, а также обрабатывает пересылку исключений и т. д. (что очень помогает).

Но рассмотрим следующий пример:

/*1*/   public async Task<int> ExampleMethodAsync()
/*2*/   {
/*3*/       var httpClient = new HttpClient(); 
/*4*/      
/*5*/       //start async task...
/*6*/       Task<string> contentsTask = httpClient.GetStringAsync("http://msdn.microsoft.com");
/*7*/   
/*8*/       //wait and return...  
/*9*/       string contents = await contentsTask;
/*10*/   
/*11*/       //get the length...
/*12*/       int exampleInt = contents.Length;
/*13*/       
/*14*/       //return the length... 
/*15*/       return exampleInt;
/*16*/   }

Если метод async (httpClient.GetStringAsync) является операцией ввода-вывода (как в моем примере выше), то я получаю следующие вещи:

  • Абонент Тема не заблокирована
  • Рабочий поток освобождается из-за операции IO ( порты завершения ввода-вывода...) (GetStringAsync использует TaskCompletionSource и не открывает новый поток)
  • Сохраненный контекст потока
  • Исключение возвращается

Но что, если вместо httpClient.GetStringAsync (операция ввода-вывода) у меня есть Task CalcFirstMillionsDigitsOf_PI_Async (операция с тяжелыми вычислениями в отдельном потоке)

Кажется, единственное, что я здесь получаю, это:

  • Сохраненный контекст потока
  • Исключение возвращается
  • Абонент Тема не заблокирована

Но у меня все еще есть другой поток (параллельный поток), который выполняет операцию. и процессор переключается между основным потоком и операцией.

Верна ли моя диагностика?


person Royi Namir    schedule 26.08.2013    source источник
comment
Обратите внимание, что обработка контекста выполняется ключевым словом await, а не ключевым словом async.   -  person xanatos    schedule 26.08.2013
comment
Комментарии @downvoter полезны.   -  person Royi Namir    schedule 26.08.2013


Ответы (3)


На самом деле в обоих случаях вы получаете только второй набор преимуществ. await не запускает асинхронное выполнение чего-либо, это просто ключевое слово для компилятора для генерации кода для обработки завершения, контекста и т.д.

Вы можете найти лучшее объяснение этого в '"Вызовите метод с ожиданием"... тьфу!' Стивена Тоуба.

Сам асинхронный метод должен решить, как он достигает асинхронного выполнения:

  • Некоторые методы будут использовать задачу для запуска своего кода в потоке ThreadPool,
  • Некоторые будут использовать какой-либо механизм завершения ввода-вывода. Для этого даже существует специальный ThreadPool, который вы можете использовать с задачами с настраиваемым TaskScheduler
  • Некоторые оборачивают TaskCompletionSource поверх другого механизма, такого как события или обратные вызовы.

В каждом случае именно конкретная реализация освобождает поток (если он используется). TaskScheduler автоматически освобождает поток, когда задача завершает выполнение, поэтому вы в любом случае получаете эту функциональность для случаев № 1 и № 2.

Что происходит в случае № 3 для обратных вызовов, зависит от того, как выполняется обратный вызов. В большинстве случаев обратный вызов выполняется в потоке, управляемом какой-либо внешней библиотекой. В этом случае вы должны быстро обработать обратный вызов и вернуться, чтобы позволить библиотеке повторно использовать метод.

ИЗМЕНИТЬ

Используя декомпилятор, можно увидеть, что GetStringAsync использует третий вариант: он создает TaskCompletionSource, который получает сигнал о завершении операции. Выполнение операции делегируется HttpMessageHandler.

person Panagiotis Kanavos    schedule 26.08.2013
comment
Первый набор включает в себя тот факт, что операция ввода-вывода выполняется драйвером, а не потоком, поэтому поток не ожидает там. здесь нет работающего потока, а в другом примере (PI) - есть работающий поток. ....пожалуйста, объясните, почему вы говорите: вы получаете только второй набор преимуществ в обоих случаях... - person Royi Namir; 26.08.2013
comment
Это метод, который запускает и использует операцию ввода-вывода и возвращает Task. await ничего не запускает. Проверьте редактирование для некоторых декомпиляторов, специально посвященных GetStringAsync. - person Panagiotis Kanavos; 26.08.2013
comment
Да я тоже только что увидел. он использует TaskCompletionSource. он не работает в рабочем потоке, поэтому второй элемент в моем первом наборе неверен. - person Royi Namir; 26.08.2013

Ваш анализ верен, хотя формулировка во второй части звучит так, как будто async создает для вас рабочий поток, а это не так.

В коде библиотеки вы действительно хотите, чтобы ваши синхронные методы были синхронными. Если вы хотите использовать синхронный метод асинхронно (например, из потока пользовательского интерфейса), вызовите его, используя await Task.Run(..)

person Stephen Cleary    schedule 26.08.2013
comment
во второй части я имел в виду : Task.Run(()=>CalcFirstMillionsDigitsOf_PI_Async()) - person Royi Namir; 26.08.2013

Да, ты прав. Я не могу найти неправильное утверждение в вашем вопросе. Просто термин "Сохраненный контекст потока" мне неясен. Вы имеете в виду «логический поток управления»? В таком случае я бы согласился.

Что касается примера с привязкой к ЦП: обычно вы не делаете этого таким образом, потому что запуск задачи на основе ЦП и ожидание ее увеличивают накладные расходы и снижают пропускную способность. Но это может быть допустимо, если вам нужно разблокировать вызывающую сторону (например, в случае проекта WinForms или WFP).

person usr    schedule 26.08.2013
comment
Итак, вы говорите, что если это вычислительная задача с высокой нагрузкой на ЦП, то я не должен ее ждать - и вместо этого я могу использовать задачу в новом потоке? - person Royi Namir; 26.08.2013
comment
Просто вызовите CalcFirstMillionsDigitsOf_PI напрямую без какой-либо асинхронности. - person usr; 26.08.2013
comment
но тогда интерфейс зависнет - person Royi Namir; 26.08.2013
comment
Да, как я и сказал. Если вы находитесь в UI-контексте, не делайте этого. Начните новую задачу и ждите ее. Скажем, в ASP.NET вам это не понадобится. - person usr; 26.08.2013
comment
похоже, что в asp.net я буду использовать его (async-await) только для операции I/O, хотя .... (и я использую asp.net). - person Royi Namir; 26.08.2013
comment
Это в основном правильно. И я рекомендую вам не использовать асинхронность в ASP.NET, пока у вас не будет доказательств того, что она вам нужна, потому что у вас будет слишком много запущенных потоков. ASP.NET — это совсем другой сценарий для асинхронности, чем клиентские приложения. Используйте асинхронность только в том случае, если дополнительные усилия того стоят. - person usr; 26.08.2013
comment
давайте продолжим это обсуждение в чате - person Royi Namir; 26.08.2013