2015-05-31 6 views
5

Я не могу сформировать ментальную картину того, как происходит поток управления с помощью икры.Что такое boost :: asio :: spawn do?

  1. Когда я называю spawn(io_service, my_coroutine), это добавить новый обработчик в io_service очереди, оборачивает вызов в my_coroutine?

  2. Когда внутри coroutine я вызываю функцию async, передающую ее мне yield_context, приостанавливает ли она сопрограмму до тех пор, пока операция async не завершится?

    void my_coroutine(yield_context yield) 
    { 
        ... 
        async_foo(params ..., yield); 
        ... // control comes here only once the async_foo operation completes 
    }

То, что я не понимаю, как мы можем избежать ожидания. Скажем, если my_coroutine обслуживает TCP-соединение, как завершаются другие экземпляры my_coroutine, вызываемые в то время как на конкретном экземпляре приостановлено, ожидая завершения async_foo?

ответ

17

Вкратце:

  1. Когда spawn() вызывается, Boost.Asio выполняет некоторые настройки работы, а затем будет использовать strand к dispatch() внутренний обработчик, который создает сопрограмму с помощью пользователя, предоставленную функцию в качестве точки входа. При определенных условиях внутренний обработчик может быть вызван при вызове spawn(), а в других случаях он будет отправлен на io_service для отложенного вызова.
  2. coroutine приостанавливается до тех пор, пока операция не завершится, а обработчик завершения не будет вызван, io_service будет уничтожен или Boost.Asio обнаруживает, что сопрограмма приостановлена, и нет возможности возобновить ее, после чего Boost.Asio уничтожит сопрограммы.

Как упоминалось выше, когда spawn() вызывается, Boost.Asio выполняет некоторые настройки работы, а затем будет использовать strand к dispatch() внутренний обработчик, который создает сопрограмму с помощью пользователя, предоставленную функцию в качестве точки входа. Когда объект yield_context передается в качестве обработчика асинхронных операций, Boost.Asio будет вывести сразу после запуска асинхронной операции с обработчиком завершения, который скопирует результаты, и резюме сопроцессор. Ранее упомянутая нить принадлежит сопрограмме, которая используется для обеспечения прибыли встречается до резюме. Давайте рассмотрим простой пример demonstratingspawn():

#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. 
+0

Что делает акт копирования 'yield_context', как она передается в сопрограммы делать? Если 'foo' передает' yield'' 'bar',' bar' передается 'baz', а' baz' вызывает 'yield' - элемент управления возвращается прямо к' foo'? Когда вызов 'timer.async_wait'« дает », возвращается ли элемент управления обработчику сопроцессора coroutine, который затем возвращается? Когда истечет время позже, как элемент управления возвращается к 'async_wait', который затем возвращается в' my_work'? – CppNoob

+0

@CppNoob Boost.Asio's первоклассная поддержка Boost.Coroutine - довольно тонкий фасад. Вы пытаетесь понять, как Boost.Asio использует Boost.Coroutine, или как работает Boost.Coroutine? Копирование 'yield_context' просто создает другой' yield_context' (а не сопрограмму). Когда 'timer.async_wait()' выдает сопрограмму, управление переходит в левый стек сразу после точки, в которой запускается сопрограмма. Когда вызывается обработчик завершения async_wait, он возобновляет сопрограмму, заставляя выполнение прыгать в правый стек сразу после точки, в которой она была получена. –

+0

Я пытаюсь понять, как Boost Asio использует сопрограммы - не от угла реализации, а как механизм управления потоком. Я думаю, что yield_context является каналом для отказа от контроля над другим контекстом. В этом примере yield_context в my_work ссылается на контекст обработчика сопроцессора-сопроцессора, и он копируется как обработчик завершения для async_wait. Но когда выполняется обработчик завершения async_wait, элемент управления возвращается к my_work, а не обработчику сопроцессора coroutine (который к тому времени вышел). Я, очевидно, не понимаю этого, и надеюсь, что смогу описать, что не ясно. – CppNoob

Смежные вопросы