часть кода в конечном автомате, а затем вернуть управление без выполнения все-
го метода до завершения. Таким образом, при вызове
IssueClientRequestAsync
поток конструирует
NamedPipeClientStream
, вызывает
Connect
, задает значе-
ние свойства
ReadMode
, преобразует переданное сообщение в
Byte[]
и вызывает
WriteAsync
. Внутренняя реализация
WriteAsync
создает объект
Task
и воз-
вращает его
IssueClientRequestAsync
. На этой стадии оператор C#
await
вы-
зывает
ContinueWith
для объекта
Task
с передачей метода, возобновляющего
выполнение конечного автомата, после чего поток возвращает управление из
IssueClientRequestAsync
.
В будущем драйвер сетевого устройства завершит запись данных в канал. По-
ток из пула оповестит объект
Task
, что приведет к активизации метода обратного
вызова
ContinueWith
, заставляющего поток возобновить выполнение конечного
автомата. А если конкретнее, поток заново входит в метод
IssueClientRequestAsync
,
но в точке оператора
await
. Теперь наш метод выполнит сгенерированный компи-
лятором код, запрашивающий состояние объекта
Task
. В случае ошибки выдается
представляющее ее исключение. Если операция завершается успешно, оператор
await
возвращает результат. В нашем случае
WriteAsync
возвращает
Task
вместо
Task
, так что возвращаемое значение отсутствует.
794
Глава.28 .Асинхронные.операции.ввода-вывода
Далее выполнение нашего метода продолжается созданием объекта
Byte[]
и по-
следующим вызовом асинхронного метода
ReadAsync
для
NamedPipeClientStream
.
Внутренняя реализаци
ReadAsync
создает объект
Task
и возвращает его.
И снова оператор
await
вызывает
ContinueWith
для объекта
Task
с пере-
дачей метода, возобновляющего выполнение конечного автомата, и снова поток
возвращает управление из
IssueClientRequestAsync
.
В будущем сервер вернет ответ клиентской машине, драйвер сетевого устрой-
ства получит этот ответ, а поток из пула уведомит объект
Task
, кото-
рый возобновит выполнение конечного автомата. Оператор
await
заставляет
компилятор сгенерировать код, который запрашивает свойство
Result
объекта
Task
(
Int32
) и присваивает результат локальной переменной
bytesRead
, или
выдает исключение в случае ошибки. Затем выполняется оставшаяся часть кода
IssueClientRequestAsync
, которая возвращает строку результата и закрывает канал.
На этой стадии конечный автомат отработал до завершения, а уборщик мусора при
необходимости освободит память.
Так как асинхронные функции возвращают управление до того, как их конеч-
ный автомат отработает до завершения, выполнение метода, вызвавшего
Issue-
ClientRequestAsync
, продолжится сразу же после того, как
IssueClientRequestAsync
выполнит свой первый оператор
await
. Но как вызывающая сторона узнает, что
выполнение конечного автомата
IssueClientRequestAsync
завершилось? Когда вы
помечаете метод ключевым словом
async
, компилятор автоматически генерирует
код, создающий объект
Task
в начале выполнения конечного автомата; этот объект
Task
завершается автоматически при завершении конечного автомата. Заметьте, что
типом возвращаемого значения
IssueClientRequestAsync
является
Task
.
Фактически возвращается объект
Task
, который создается кодом, сгене-
рированным компилятором, а свойство
Result
объекта
Task
в данном случае имеет
тип
String
. Ближе к концу
IssueClientRequestAsync
я возвращаю строку. Это
заставляет код, сгенерированный компилятором, завершить созданный им объект
Task
и задать его свойству
Result
возвращенную строку.
Для асинхронных функций действует ряд ограничений:
Метод
Main
приложения не может быть преобразован в асинхронную функцию.
Кроме того, конструкторы, методы доступа свойств и методы доступа событий
не могут быть преобразованы в асинхронные функции.
Асинхронная функция не может иметь параметры
out
и
ref
.
Оператор
await
не может использоваться в блоке
catch
,
finally
или
unsafe
.
Не допускается установление блокировки, поддерживающей владение пото-
ком или рекурсию, до операции
await
, и ее снятие после оператора
await
. Это
ограничение объясняется тем, что один поток может выполнить код до
await
,
а другой поток может выполнить код после
await
. При использовании
await
с командой C#
lock
компилятор выдает сообщение об ошибке. Если вместо
этого явно вызвать методы
Enter
и
Exit
класса
Monitor
, то код откомпилиру-
795
Преобразование.асинхронной.функции.в.конечный.автомат
ется, но
Monitor.Exit
выдаст исключение
SynchronizationLockException
во
время выполнения.
В выражениях запросов оператор
await
может использоваться только в первом
выражении коллекции условия
from
или в выражении коллекции условия
join
.
Все эти ограничения не столь существенны. При их нарушении компилятор вы-
даст соответствующее сообщение, а проблемы обычно удается решить при помощи
незначительной модификации кода.
Do'stlaringiz bilan baham: |