Хорошо, вот мой взгляд на это.
Есть нечто, называемое сопрограммы , которое известно на протяжении десятилетий. («Кнут и Хоппер» -класс «на десятилетия») Они являются обобщениями подпрограмм , в том смысле , что они не только получают и освобождают управление в операторе запуска и возврата функции, но и делают это в определенных точках ( точках приостановки ). Подпрограмма - это сопрограмма без точек подвеса.
Их легко и просто реализовать с помощью макросов 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
это не обязательно, это сопрограмма или нормальная функция? Должен ли компилятор переписать его как сопрограмму или нет? Оба могут быть возможны с различной возможной семантикой.