Хорошо, вот мой взгляд на это.
Есть нечто, называемое сопрограммы , которое известно на протяжении десятилетий. («Кнут и Хоппер» -класс «на десятилетия») Они являются обобщениями подпрограмм , в том смысле , что они не только получают и освобождают управление в операторе запуска и возврата функции, но и делают это в определенных точках ( точках приостановки ). Подпрограмма - это сопрограмма без точек подвеса.
Их легко и просто реализовать с помощью макросов C, как показано в следующей статье о "protothreads". ( http://dunkels.com/adam/dunkels06protothreads.pdf ) Прочтите его. Я буду ждать...
Суть этого в том , что макросы создают большой switch, и caseметку в каждой точке подвески. В каждой точке приостановки функция сохраняет значение непосредственно следующей caseметки, чтобы она знала, где возобновить выполнение при следующем вызове. И это возвращает контроль звонящему.
Это делается без изменения видимого потока управления кодом, описанным в «protothread».
Теперь представьте, что у вас есть большой цикл, вызывающий все эти «протопотоки» по очереди, и вы одновременно выполняете «протопотоки» в одном потоке.
Этот подход имеет два недостатка:
- Вы не можете сохранять состояние в локальных переменных между возобновлениями.
- Вы не можете приостановить "protothread" от произвольной глубины вызова. (все точки подвеса должны быть на уровне 0)
Есть обходные пути для обоих:
- Все локальные переменные должны быть подтянуты до контекста протопотока (контекст, который уже необходим тем фактом, что протопотока должна хранить свою следующую точку возобновления)
- Если вы чувствуете, что вам действительно нужно вызвать другую протопоток из протопотока, «породить» дочернюю протопотоку и приостановить до завершения дочерней.
И если бы у вас была поддержка компилятора для выполнения работы по перезаписи, которую выполняют макросы и обходной путь, ну, вы могли бы просто написать свой код прототипа, как вы и намеревались, и вставить точки приостановки с ключевым словом.
И это то, что asyncи awaitвсе: создание сопрограмм (без стеков).
Сопрограммы в C # являются объектами (универсального или неуниверсального) класса Task.
Я нахожу эти ключевые слова очень вводящими в заблуждение. Мое умственное чтение:
async как "надёжный"
await как "приостановить до завершения"
Task как "будущее ..."
Сейчас. Нам действительно нужно пометить функцию async? Помимо того, что он должен запускать механизмы переписывания кода, чтобы сделать функцию сопрограммой, он устраняет некоторые неоднозначности. Рассмотрим этот код.
public Task<object> AmIACoroutine() {
var tcs = new TaskCompletionSource<object>();
return tcs.Task;
}
Предполагая, что asyncэто не обязательно, это сопрограмма или нормальная функция? Должен ли компилятор переписать его как сопрограмму или нет? Оба могут быть возможны с различной возможной семантикой.