В последнее время я не могу получить достаточно удивительного асинхронного паттерна C # 5.0. Где ты был всю мою жизнь?
Я просто в восторге от простого синтаксиса, но у меня есть одна небольшая сложность. Моя проблема в том, что асинхронные функции имеют совершенно другое объявление, чем обычные функции. Поскольку только другие асинхронные функции могут ожидать других асинхронных функций, когда я пытаюсь перенести некоторый старый блокирующий код в асинхронный режим, у меня возникает эффект домино функций, которые я должен преобразовать.
Люди называют это заражением зомби . Когда async откусит ваш код, он будет все больше и больше. Процесс портирования не сложен, он просто добавляет async
объявление и оборачивает возвращаемое значение Task<>
. Но раздражает делать это снова и снова при переносе старого синхронного кода.
Мне кажется, было бы гораздо более естественным, если бы оба типа функций (асинхронная и обычная старая синхронизация) имели одинаковый синтаксис. Если бы это было так, портирование потребовало бы ноль усилий, и я мог бы безболезненно переключаться между этими двумя формами.
Я думаю, что это может сработать, если мы будем следовать этим правилам:
Асинхронные функции больше не требуют
async
объявления. Их возвращаемые типы не нужно было бы оборачиватьTask<>
. Компилятор сам идентифицирует асинхронную функцию во время компиляции и при необходимости автоматически выполняет перенос Task <>.Больше не нужно запускать вызовы асинхронных функций. Если вы хотите вызвать асинхронную функцию, вам нужно дождаться ее. В любом случае я почти не использую метод «забей и забудь», и все примеры сумасшедших условий гонки или тупиков всегда основаны на них. Я думаю, что они слишком запутаны и «не имеют отношения» к синхронному мышлению, которое мы пытаемся использовать.
Если вы действительно не можете жить без огня и забыть, для него будет специальный синтаксис. В любом случае, это не будет частью простого унифицированного синтаксиса, о котором я говорю.
Единственное ключевое слово, которое вам нужно для обозначения асинхронного вызова, это
await
. Если вы ожидаете, вызов асинхронный. Если вы этого не сделаете, вызов будет старым синхронным (помните, у нас больше нет возможности «забыть»).Компилятор автоматически идентифицирует асинхронные функции (поскольку у них больше нет специального объявления). Правило 4 делает это очень простым - если функция имеет
await
вызов внутри, она асинхронная.
Может ли это работать? или я что-то упустил? Этот унифицированный синтаксис является гораздо более гибким и может решить проблему заражения зомби.
Некоторые примеры:
// assume this is an async function (has await calls inside)
int CalcRemoteBalanceAsync() { ... }
// assume this is a regular sync function (has no await calls inside)
int CalcRemoteBalance() { ... }
// now let's try all combinations and see what they do:
// this is the common synchronous case - it will block
int b1 = CalcRemoteBalance();
// this is the common asynchronous case - it will not block
int b2 = await CalcRemoteBalanceAsync();
// choosing to call an asynchronous function in a synchronous manner - it will block
// this syntax was used previously for async fire-and-forget, but now it's just synchronous
int b3 = CalcRemoteBalanceAsync();
// strange combination - it will block since it's calling a synchronous function
// it should probably show a compilation warning though
int b4 = await CalcRemoteBalance();
Примечание: это продолжение интересного связанного обсуждения в SO
await
немедленно. Вы можете сделать что-то вроде var task = FooAsync(); Bar(); await task;
. Как бы я сделал это в вашем предложении?
async
. Я думаю , что это одна из самых больших преимуществ async
- await
: что позволяет легко сочинять асинхронные операции (а не только в самой простой «начать, дождитесь, запуск B, ожидание B» путь). И для этого уже есть специальный синтаксис: он называется await
.