2016-08-30 1 views
2

Wenn ich boost::asio::strandwrap Funktion zu nutzen versucht, so scheint es, die shared_ptr zu verursachen, die von shared_from_this kamen NULL zu werden, so dass, wenn es zum zweiten Mal ausgeführt wird, Es wird dazu führen, dass das Programm abstürzt.boost :: asio :: Strangverpackungs Shared_ptr von shared_from_this verursacht NULL werden

Dies geschieht jedoch nur, wenn die Funktion, die gebunden wurde keine Argumente.

Nachdem die Uhr Punkt Einstellung und Überprüfung der Aufrufliste, scheint dieses Verhalten durch Bewegung Konstruktor shared_ptr Austauschwert verursacht werden, die von async_result_init mit BOOST_ASIO_MOVE_CAST entstanden.

Meine Frage ist, wie sollte ich wrap richtig verwenden, um dies zu vermeiden?

Es folgt ein einfaches Beispiel, Ihnen zu zeigen, was ich meinte:

class object : public boost::enable_shared_from_this<object> { 
public: 
    object() 
    : service(new boost::asio::io_service), 
    work(new boost::asio::io_service::work(*service)), 
    strand(*service), 
    value(0) {} 

    void workerThread() { 
     service->run(); 
    } 

    void run() { 
     func_int = strand.wrap(boost::bind(&object::handler_int, shared_from_this(), _1)); 
     func_void = strand.wrap(boost::bind(&object::handler_void, shared_from_this())); 

     std::thread thread(boost::bind(&object::workerThread, this)); 

     func_int(1); 
     func_int(1); 

     func_void(); 
     func_void(); // Will crash due to shared_ptr being NULL, hence "value" cannot be accessed in handler_void 

     thread.join(); 
    } 

    void handler_int(int parameter) { 
     cout << "handler_int: " << value << endl; 
    } 

    void handler_void() { 
     cout << "handler_void: " << value << endl; 
    } 

    boost::shared_ptr<boost::asio::io_service> service; 
    boost::shared_ptr<boost::asio::io_service::work> work; 
    boost::asio::strand strand; 
    std::function<void(int)> func_int; 
    std::function<void(void)> func_void; 
    int value; 
}; 


int main(int argc, char *argv[]) {  
    boost::shared_ptr<object> obj(new object()); 
    obj->run(); 
    return 0; 
} 
+0

Sie nicht das Arbeitsobjekt korrekt initialisieren. –

+0

@RichardHodges: Vielen Dank für Ihre Antwort, aber ich bin mir nicht sicher, ob das der Grund ist. Ich habe 'Arbeit' so auch initialisiert gesehen: [link] (http://www.boost.org/doc/libs/1_45_0/doc/html/boost_asio/example/services/logger_service.hpp) – Bill

+0

verstanden. Es ist ein schlechter Stil, aber die wirkliche Antwort ist unten. –

Antwort

3

Dies beruht auf einem Missverständnis dessen, was wrap tut. Das hat mich zu kürzlich erwischt. Ich schrieb an den Autor von Asio zur Klarstellung. Was er mir erzählte, überraschte mich.

wrap gibt einen ‚wrapped_handler‘, aber im Gegensatz zu dem, was man erwarten könnte, dies kein Funktionsobjekt ist die eine Depesche unter der Decke führt.

Es ist eigentlich ein Objekt, das den gebundenen Handler und einen Verweis auf den Executor enthält. Asio-IO-Objekte verwenden diese Informationen beim Abschließen von asynchronen Operationen, um den Handler im richtigen Kontext auszuführen.

Verwirrenderweise hat diese wrapped_handler auch eine operator(). Was dies tut, ist lediglich die gebundene Funktion auszuführen. Es tut nicht Post oder senden Sie die Funktion. Aus meiner Sicht sollte diese operator() nicht existieren. Ich habe Christopher Kohlhoff diese Meinung gesagt. Ich habe keine Antwort erhalten.

Sie können dies dies durch den Austausch unter Beweis stellen:

func_int(1); 

mit diesem:

service->post(strand->wrap(boost::bind(&object::handler_int, shared_from_this(), 1))); 

für jede der Anrufungen der gebundenen Mitgliedsfunktion. Sie können dann testen, dass sie im Strang ausführen.

Beachten Sie, dass es egal wäre, welche io_service Sie/entsendet haben. Sie würden direkt zu dem Strang geschickt werden, mit dem sie durch wrap verbunden wurden.

Endlich, auf Asio und Stil.

Möglicherweise möchten Sie die Eigentumsrechte an io_service/strand/work-Objekten durch shared_ptr adressieren. Das ist immer unnötig. Ein io_service ist eine grundlegende Komponente einer Anwendung - die zentrale Nachrichtenschleife. Es wird keine unbestimmte Lebensdauer haben. Weder wird die Arbeit oder der Strang. Vorausgesetzt, Sie beenden die Lebensdauer mit:

service.stop(); 
thread(s).join(); 

Alles wird gut.

+0

Vielen Dank für die ausführliche Erklärung. Als ich jedoch beim Debugging die Objektstruktur am Unterbrechungspunkt betrachtete, ahnte ich, dass es sich um einen Wrapper um die ursprüngliche Funktion handelte, da ich die Variablen "dispatcher_" und "handler_" sah. Auch im Speicher ist die Adresse meines ursprünglichen 'Objekt'-Zeigers mehr als einmal aufgetaucht. Was ich wissen wollte ist, warum es sich unterscheidet, wenn die Anzahl der Argumente unterschiedlich ist, und wie man 'wrap' richtig verwendet. – Bill

+0

Wie für 'service-> post ...', habe ich es getestet, und wenn ich einen zweiten Dienst erstellen und 'service2-> post' aufrufen, wird die Funktion im service2 (mit Thread-ID überprüft), nicht aufgerufen der "Strang", mit dem es verbunden war, denn in Ihrem Beispiel haben Sie es nicht mit dem "Strang" bezeichnet. – Bill

+0

Soweit ich weiß, kann io_service nicht kopiert werden. Um sicherzustellen, dass es weitergegeben werden kann, wickelt man es um ein shared_ptr herum, so dass es aus meiner Sicht nicht wirklich unnötig ist. – Bill