Comments 25
выталкивания данныхПроталкивание лучше подходит?
Такая большая статья, а самые интересные и полезные вещи вы оставили где то по ссылкам.
Исправляем ситуацию… Допустим, у вас есть следующий метод
IEnumerable<FileResult> ReadFiles(string[] fileNames)
{
foreach(var name in fileNames)
{
yield return ReadFile(name);
}
}
Видите ключевое слово yield? В нем все дело.
Теперь вы узнали о чудесной конструкции async await и хотели бы применить ее к данному методу. И у вас даже есть новый метод ReadFileAsync.
Вы хотели бы, конечно, написать что-то такое
async IAsyncEnumerable<FileResult> ReadFilesAsync(string[] fileNames)
{
foreach(var name in fileNames)
{
yield return await ReadFileAsync(name);
}
}
Так вот! Раньше так нельзя было написать. А в C# 8 можно.
А что там принципиально разного?
Например поэтому в C# есть такой твик как ConfigureAwait()
Ну так это особенности рантайма вообще, а не особенности async/await.
И да, про задачу лучше не говорить что она "может выполняться в другой нити" — создаётся впечатление, что задача всегда выполняется в какой-то нити, а это не так.
Ещё один отличием является то обстоятельство что Promise`ы и Task`и не эквиваленты по фичам. Task`и подразумевают отмену операции, соответственно в месте await`а можно получить OperationCanceledException, в js ничего похоже в стандартизированного нет.
Асинхронные Stream в C# 8 как я понимаю не поддерживают обратного давления и для более или менее житейский ситуаций нужно использовать Rx?
Обратное давление как раз получится, если читать медленно
Нет это методу который осуществляет вызов, нужно быть осторожнее и следовательно следить за реактивностью вызванного, следующего по цепочке, метода. Это увы, ручное управление давлением.
Но практика для десктопных приложений показывает, что отгрузка в параллельный поток и чтение обычным синхронным способом работает в разы быстрее. Особенно на слабом железе типо IoT или телефон. Плюс в параллельном потоке можно еще что-то поделать, посчитать там что-то полезное, данные преобразовать, не нагружая при этом основной поток.
А основной поток засирается диспетчингом всех этих асинков очень неслабо.
Один Task.Run
в нужном месте (или ConfigureAwait(false)
) — и вот уже основной поток не нагружается.
ConfigureAwait(false) отрывает Task от изначального синхронизационного контекста, бросая продолжения на ThreadPool, я так понимаю. Это значит, аллокировать делегат продолжения, зафиксировать все используемые локальные переменные из окружения, найти в ThreadPool свободный поток (а это thread safe, значит лочить надо), заинициализировать все статические переменные потока, чтобы Task-и работали нормально. Плюс там все через StateMachine сделано, т.е. каждый await это switch по int.
Надо замерять производительность. Меня сильно беспокоит то, что в UWP они начинают все API асинхронным делать, без альтернативных синхронных методов. Добавить сюда еще постоянный COM marshalling в сишное ядно, так недолго добиться JavaScript производительности. :)
Нет, ConfigureAwait(false)
исполняет продолжение в том же самом потоке, в котором завершилась задача, если только задача не была создана с флагом RunContinuationsAsync
Может тяготеть исполнять в том же потоке — да, но это не гарантируется.
Асинхронные Stream в C# 8