Страницы

Поиск по вопросам

среда, 4 декабря 2019 г.

Async и Await. Контекст синхронизации и выполнения.Конечный автомат. C#

#c_sharp #net #async_await


Добрый день.Читаю про async и await (Джон Скит) и не могу никак понять момент про
контекст синхронизации и контекст выполнения.Может кто - нибудь изложить или направить
где почитать об этом более простым языком.Я правильно понимаю что async-await не создает
новый поток,а работает в текущем ? И как при этом помогает ConfigureAwait
    


Ответы

Ответ 1



Нет-нет, async/await вообще не работает с потоками. Смотрите, на самом деле async-функция — это вовсе не функция в привычном понимании этого слова. С внешней стороны она выглядит как функция, возвращающая Task. Но если мы посмотрим изнутри, то приход наружу возвращаемого значения не означает, что код функции добежал до return или конца тела функции! Из async-функции возвращается обычно незавершённый Task. Стойте, а что это за зверь — Task? Это абстракция, которая представляет собой какое-то задание, выполнение которого завершится в какой-то момент.* Для async-функции задание — это выполнить саму функцию, и это самое задание завершится в момент, когда функция с точки зрения своего внутреннего выполнения добежит до return. Так вот, пусть у нас есть async-функция F: async Task F() { var seconds = new Random().Next(10); await Task.Delay(TimeSpan.FromSeconds(seconds)); } Когда мы пишем Task t = F();, то присвоение переменной t происходит не когда функция добежит до конца, а практически сразу**. А что соответствует окончанию работы Task'а? Очень просто, но это важный момент. Когда вы напишете await t; (а вы можете написать это только в другом, внешнем async-методе!), то выполнится эта строка тогда, когда наш Task завершится. Причём на время ожидания завершения t, наше внешнее задание не будет блокировать поток, ожидая окончания внутреннего задания t. На время ожидания наше внешнее задание как бы не будет выполняться нигде, и будет существовать лишь в форме объекта. По завершению задания t наше внешнее задание снова «появится» и продолжит выполнение, как ни в чём ни бывало. А причём же здесь многопоточность, спросите вы. Ну да, она тут практически не при чём. Внутреннее задание t может выполняться в том же, в другом, в нескольких или вообще ни в каком потоке, для await это неважно, await просто дожидается окончания задания t. Просто раньше, до появления async/await для того, чтобы функция на время ожидания не блокировала всё приложение, приходилось выгружать её в другой поток. Но теперь это не нужно, т. к. async-функции на время ожидания по await не блокируют ничего, и той же функциональности можно добиться и без многопоточности. Ещё два слова про ConfigureAwait. Когда async-функция возобновляет выполнение после await, в каком потоке она это делает? Ну вообще-то это не обязательно исходный поток, в котором она начинала свой await (этот поток вообще может уже завершиться!). Но есть важный частный случай: если у нас графическое приложение, и await начинался в UI-потоке, то продолжаться будет тоже в нём (за это отвечает тот самый контекст выполнения). Обычно это важно, т. к. доступ к UI-элементам возможен только в UI-потоке. Также есть и другие ситуации, когда вам хотелось бы вернуться после await в тот же поток***, в котором вы начинали await, Так вот, если вам это не нужно, если вам всё равно, где будет происходить выполнение продолжения задания, то вы можете воспользоваться ConfigureAwait(false), чтобы сообщить об этом фреймворку и немного облегчить ему задачу. Вот и всё. Он может этим воспользоваться, и перекинуть хвост задания на пул потоков. А может и не воспользоваться, его право. Дополнительное чтение по теме: Нужен async/await или не нужен? Как работают await async Андрей Часовских — Async/await: собираем грабли *Ну или никогда не завершится. Например, внутри async-функции можно легко устроить бесконечный цикл. **Когда именно, можно посмотреть в этом более подробном описании. ***Или группу потоков. Или вообще SynchronizationContext.

Комментариев нет:

Отправить комментарий