2015-05-31 20 views
5

Ich bin nicht in der Lage, ein mentales Bild davon zu bilden, wie der Kontrollfluss mit dem Spawn passiert.Was macht boost :: asio :: spawn?

  1. Als ich spawn(io_service, my_coroutine) nennen, macht es einen neuen Handler in die io_service Warteschlange hinzufügen, die einen Aufruf an die my_coroutine wickelt?

  2. Wenn ich innerhalb der Coroutine eine asynchrone Funktion anrufe, die meine yield_context übergibt, setzt sie die Coroutine aus, bis die asynchrone Operation abgeschlossen ist?

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

Was ich nicht verstehe, ist, wie wir wartet vermeiden. Sagen Sie, wenn my_coroutine eine TCP-Verbindung bedient, wie werden andere Instanzen von my_coroutine aufgerufen, während eine bestimmte Instanz gesperrt ist, warten auf async_foo abzuschließen?

Antwort

17

Kurz:

  1. Wenn spawn() aufgerufen wird, führt Boost.Asio einige Arbeit Einrichtung und anschließend einen strand zu dispatch() einen internen Handler benutzen, die eine Koroutine der Funktion als Einstiegspunkt bereitgestellt Benutzer unter Verwendung erzeugt. Unter bestimmten Bedingungen kann der interne Handler innerhalb des Anrufs zu spawn() aufgerufen werden, und zu anderen Zeiten wird er zum verzögerten Aufruf an die Nummer io_service gesendet.
  2. Die Coroutine wird angehalten, bis entweder die Operation abgeschlossen ist und der Completion-Handler aufgerufen wird, io_service zerstört ist oder Boost.Asio erkennt, dass die Coroutine angehalten wurde und keine Möglichkeit gibt, sie fortzusetzen, zu diesem Zeitpunkt wird Boost.Asio zerstört die Coroutine.

Wie oben erwähnt, wenn spawn() aufgerufen wird, führt Boost.Asio einige Arbeit Einrichtung und anschließend einen strand zu dispatch() einen internen Handler benutzen, die eine Koroutine der Funktion als Einstiegspunkt bereitgestellt Benutzer unter Verwendung erzeugt. Wenn das Objekt als yield_context Handler asynchrone Operationen übergeben wird, ergibt Boost.Asio wird unmittelbar nach dem asynchronen Betrieb mit einem Abschluss-Handler, die Ergebnisse und Initiieren resume die Koroutine kopiert. Der zuvor erwähnte Strang gehört der Coroutine, die verwendet wird, um die Ausbeute vor resume zu garantieren.

#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(); 
} 

Das obige Beispiel gibt: Ein einfaches Beispiel demonstratingspawn() Lets betrachten

Start wait 
Other work 
Woke up 

Hier ist ein Versuch, die Ausführung des Beispiels zu erläutern. Paths in | zeigt die aktiven Stapel, : die suspendierten Stapel zeigen an, und Pfeile verwendet werden Übertragung der Kontrolle, um anzuzeigen:

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

Was bedeutet den Akt des Kopierens 'yield_context', wie es zu einem Koroutine tun geben wird? Wenn 'foo'' yield' zu 'bar' übergibt, wird' bar' an 'baz' übergeben und' baz' ruft 'yield' auf - geht das Steuerelement direkt zurück zu' foo'? Wenn der 'timer.async_wait' Aufruf" liefert ", geht die Steuerung zurück zum Coroutine-Creator-Handler, der dann zurückgibt? Wenn die Zeit später abläuft, wie geht die Steuerung zurück zu 'async_wait' - die dann zu' my_work' zurückkehrt? – CppNoob

+0

@CppNoob Boost.Asio's erstklassige Unterstützung für Boost.Coroutine ist eine ziemlich dünne Fassade. Versuchen Sie zu verstehen, wie Boost.Asio Boost.Coroutine verwendet oder wie Boost.Coroutine selbst funktioniert? Das Kopieren von yield_context erzeugt nur einen weiteren yield_context (keine Coroutine). Wenn die 'timer.async_wait()' die Coroutine liefert, springt die Steuerung zum linken Stapel unmittelbar nach dem Punkt, an dem die Coroutine gestartet wurde. Wenn der Beendigungshandler 'async_wait' aufgerufen wird, wird die Coroutine fortgesetzt, was bewirkt, dass die Ausführung unmittelbar nach dem Punkt, an dem sie ausgeführt wurde, zum rechten Stapel springt. –

+0

Ich versuche zu verstehen, wie Boost Asio Koroutinen verwendet - nicht aus einem Implementierungswinkel, sondern als ein Kontrollflussmechanismus. Ich denke an den yield_context als einen Kanal, um die Kontrolle an einen anderen Kontext abzugeben. In diesem Beispiel bezieht sich der yield_context in my_work auf den Coroutine-Creator-Handlerkontext und wird als Beendigungshandler für async_wait kopiert. Aber wenn der Beendigungshandler von async_wait ausgeführt wird, kehrt das Steuerelement zu my_work zurück, nicht zum Coroutiny-Creator-Handler (der bis dahin beendet wurde). Ich verstehe das offensichtlich nicht und ich hoffe, ich könnte beschreiben, was nicht klar ist. – CppNoob