Предположим, у меня есть две сопрограммы: coroutine_a
и coroutine_b
. В coroutine_a
он вызывает:
co_await awaiter_a;
awaiter_a::await_suspend
возвращает обработчик сопрограммы coroutine_b
. В результате coroutine_b
возобновляется. Затем в coroutine_b
он вызывает:
co_await awaiter_b;
awaiter_b::await_suspend
возвращает недействительность. В результате управление должно быть возвращено вызывающей стороне/возобновляющей стороне coroutine_a
. Что произойдет, когда управление вернется к coroutine_a
?
Я думаю, что есть два варианта:
coroutine_a
возвращается к вызывающему абоненту.coroutine_a
остается приостановленным, пока кто-нибудь не возобновит его.Какой из них правильный? Или что-то еще?
@RaymondChen Похоже, это может быть ответом.
@RaymondChen Спасибо за ответ. Это именно то, чего я хочу.
co_await awaiter_a;
Запись этого кода в функцию означает, что функция ожидает значения, которое будет сгенерировано awaiter_a
. Следовательно, этой текущей функции не разрешено продолжать работу до тех пор, пока awaiter_a
не сгенерирует это значение.
Если await_suspend
возвращает дескриптор сопрограммы, это означает, что выполнение продолжается в этом дескрипторе сопрограммы. Однако это не меняет смысла приостановки; текущая сопрограмма остановится до тех пор, пока awaiter_a
не будет готова выдать значение.
Фактически это означает, что новый дескриптор (из вашего coroutine_b
) заменяет текущий дескриптор (из вашего coroutine_a
, который только что приостановился).
Если await_suspend
ничего не возвращает, это просто приостанавливает работу сопрограммы. Поэтому, когда coroutine_b
выполняет co_await
, он приостанавливается. Но это все. Поток управления продолжается от вызывающей стороны coroutine_a
, как если бы coroutine_a
был приостановлен... потому что он был и до сих пор приостановлен.
Это объясняется [expr.await]/5:
Выражение-await оценивает (возможно, преобразованное) выражение o и выражение await-ready, затем:
Если результат await-ready равен
false
, сопрограмма считается приостановленной. Затем:
- Если тип await-suspend равен
std::coroutine_handle<Z>
, оценивается await-suspend.resume()
.
[Примечание 1: Это возобновляет сопрограмму, на которую ссылается результат ожидания-приостановки. Таким образом можно последовательно возобновлять любое количество сопрограмм, в конечном итоге возвращая поток управления текущему вызывающему или возобновляющему сопрограмму ([dcl.fct.def.coroutine]). — последнее примечание]- В противном случае, если тип await-suspend равен
bool
, оценивается await-suspend, и выполнение сопрограммы возобновляется, если результат равенfalse
.- В противном случае оценивается await-suspend.
Если вычисление await-suspend завершается через исключение, исключение перехватывается, сопрограмма возобновляется, и исключение немедленно генерируется повторно ([Exception.throw]). В противном случае поток управления возвращается к текущему вызывающему или возобновляющему сопрограмму ([dcl.fct.def.coroutine]) без выхода из каких-либо областей ([stmt.jump]). Точка в сопрограмме непосредственно перед возвратом управления к вызывающему или возобновляющему объекту является точкой приостановки сопрограммы.
Если результатом await-ready является
true
или когда сопрограмма возобновляется не путем повторного создания исключения из await-suspend, вычисляется выражение await-resume, и его результатом является результат await-expression.
Итак, co_await awaiter_a
сначала приостанавливает сопрограмму A, затем переходит к первому подпункту и вызывает .resume()
дескриптор сопрограммы B. Когда сопрограмма B приостанавливается и возвращает управление сопрограмме A, то мы только что завершили второй подпункт в сопрограмме A, который означает, что мы переходим к абзацу в конце первого маркера, и поток управления возвращается к вызывающему или возобновляющему сопрограмму A.
(Примечание 1 также объясняет это. Возврат дескриптора сопрограммы из await-suspend по сути просто вставляет сопрограмму в начало очереди, которую нужно возобновить, перед сопрограммой, которая в противном случае была бы возобновлена, если бы await-suspend имел тип void
.)
Симметричная передача означает, что когда `await_suspend`
coroutine_a
возвращаетcoroutine_b
,coroutine_a
покидает изображение, и его вызывающая сторона становится вызывающейcoroutine_b
.