Я не могу сформировать мысленную картину того, как поток управления происходит с порождением.
Когда я звоню spawn(io_service, my_coroutine)
это добавляет новый обработчик к io_service
очередь, которая оборачивает вызов my_coroutine
?
Когда внутри сопрограммы я вызываю асинхронную функцию, передавая ее мой yield_context
, приостанавливает ли сопрограмму, пока не завершится асинхронная операция?
void my_coroutine (yield_context yield) { ... async_foo (params ..., yield); ... // управление приходит сюда только после завершения операции async_foo }
Чего я не понимаю, так это как мы избегаем ожидания. Скажи если my_coroutine
обслуживает соединение TCP, как другие экземпляры my_coroutine
вызывается в то время как на конкретном экземпляре приостановлено, ожидая async_foo
завершить?
Короче:
spawn()
Boost.Asio выполняет некоторые настройки и затем использует strand
в dispatch()
внутренний обработчик, который создает сопрограмму, используя предоставленную пользователем функцию в качестве точки входа. При определенных условиях внутренний обработчик может быть вызван в вызове spawn()
и в других случаях он будет опубликован на io_service
для отложенного вызова.io_service
уничтожен или Boost.Asio обнаруживает, что сопрограмма была приостановлена без возможности возобновления, после чего Boost.Asio уничтожит сопрограмму.Как упоминалось выше, когда spawn()
Boost.Asio выполняет некоторые настройки и затем использует strand
в dispatch()
внутренний обработчик, который создает сопрограмму, используя предоставленную пользователем функцию в качестве точки входа. Когда yield_context
объект передается в качестве обработчика асинхронных операций, Boost.Asio Уступать сразу после запуска асинхронной операции с обработчиком завершения, который будет копировать результаты и продолжить сопрограмма. Ранее упомянутая цепь принадлежит сопрограмме и используется для гарантии Уступать происходит раньше продолжить. Давайте рассмотрим простой пример демонстрирующий spawn()
:
#include <iostream>
#include <boost/asio.hpp>
#include <boost/asio/spawn.hpp>
boost::asio::io_service io_service;
void other_work()
{
std::cout << "Other work" << std::endl;
}
void my_work(boost::asio::yield_context yield_context)
{
// Add more work to the io_service.
io_service.post(&other_work);
// Wait on a timer within the coroutine.
boost::asio::deadline_timer timer(io_service);
timer.expires_from_now(boost::posix_time::seconds(1));
std::cout << "Start wait" << std::endl;
timer.async_wait(yield_context);
std::cout << "Woke up" << std::endl;
}
int main ()
{
boost::asio::spawn(io_service, &my_work);
io_service.run();
}
Приведенный выше пример выводит:
Start wait
Other work
Woke up
Вот попытка проиллюстрировать выполнение примера. Пути в |
указать активный стек, :
указывает на приостановленный стек, а стрелки используются для обозначения передачи управления:
boost::asio::io_service io_service;
boost::asio::spawn(io_service, &my_work);
`-- dispatch a coroutine creator
into the io_service.
io_service.run();
|-- invoke the coroutine creator
| handler.
| |-- create and jump into
| | into coroutine ----> my_work()
: : |-- post &other_work onto
: : | the io_service
: : |-- create timer
: : |-- set timer expiration
: : |-- cout << "Start wait" << endl;
: : |-- timer.async_wait(yield)
: : | |-- create error_code on stack
: : | |-- initiate async_wait operation,
: : | | passing in completion handler that
: : | | will resume the coroutine
| `-- return <---- | |-- yield
|-- io_service has work (the : :
| &other_work and async_wait) : :
|-- invoke other_work() : :
| `-- cout << "Other work" : :
| << endl; : :
|-- io_service still has work : :
| (the async_wait operation) : :
| ...async wait completes... : :
|-- invoke completion handler : :
| |-- copies error_code : :
| | provided by service : :
| | into the one on the : :
| | coroutine stack : :
| |-- resume ----> | `-- return error code
: : |-- cout << "Woke up." << endl;
: : |-- exiting my_work block, timer is
: : | destroyed.
| `-- return <---- `-- coroutine done, yielding
`-- no outstanding work in
io_service, return.