2016-08-05 5 views
2

Ich habe ein Problem bei der Verwendung von deadline_timer und io_service :: Beitrag wie folgt:Verwendung io_service :: Post (Boost), wenn deadline_timer wartet

#include "boost/asio.hpp" 
#include "boost/thread.hpp" 
int main() 
{ 
    boost::asio::io_service io_service; 

    boost::asio::deadline_timer timer1(io_service); 
    boost::asio::deadline_timer timer2(io_service); 

    timer1.expires_from_now(boost::posix_time::seconds(1)); 
    timer1.async_wait([](const boost::system::error_code& error) { 
     boost::this_thread::sleep(boost::posix_time::seconds(5)); 
     printf("1 "); 
    }); 

    timer2.expires_from_now(boost::posix_time::seconds(2)); 
    timer2.async_wait([](const boost::system::error_code& error) { 
     printf("2 "); 
    }); 

    boost::thread t([&io_service]() { 
     boost::this_thread::sleep(boost::posix_time::seconds(5)); 
     io_service.post([]() { 
      printf("3 "); 
     }); 
     io_service.post([]() { 
      printf("4 "); 
     }); 
    }); 

    io_service.run(); 
    t.join(); 
    getchar(); 

    return 0; 
} 

ich thougth, dass das Ergebnis "1 2 3 4" ist aber Das Ergebnis ist "1 3 4 2". Jeder kann mir zeigen, wie der Rückruf von Timer2 (Druck "2") zuvor als Ergebnis "1 2 3 4" mit Boost-Bibliothek durchgeführt wird (und die Ablaufzeit von Timer1 und Timer2 nicht ändern).

Vielen Dank!

Antwort

0

Dies ist eigentlich ein ziemlich kompliziertes Beispiel.

Die io_service wird auf dem Hauptthread ausgeführt. Hier ist die Reihenfolge der Operationen

Haupt Thema:

  • Anfrage Timer bei T0 + 1
  • Anfrage Timer bei T0 + 2
  • Spawn Gewinde
  • Führen Sie alle anstehenden io (io_service.run())

Sekundäres Gewinde:

  • Schlaf 5 Sekunden
  • Anfrage Timer
  • Anfrage Timer

Zunächst einmal nichts mehr im io_service bis io_service.run() genannt ausführen wird.

Sobald io_service.run() aufgerufen wird, wird ein Timer für 1 Sekunde in der Zukunft geplant. Wenn dieser Timer ausgelöst wird, wird er zunächst für 5 Sekunden vor dem Drucken von 1 gespeichert.

Während dieser Thread ausgeführt wird, wird auch der sekundäre Thread angezeigt und für 5 Sekunden inaktiviert. Dieser Thread wird eingerichtet und geplant, bevor der im Handler für timer1 ausgeführte Zeitgeber abgeschlossen ist. Da beide Threads für 5 Sekunden schlafen, werden "2" und "3" sofort an die io_service gebucht.

Jetzt wird es ein bisschen schwierig. Es scheint wahrscheinlich, dass das Timeout für timer2 inzwischen abgelaufen sein sollte (es ist mindestens 5 Sekunden in der Zukunft), aber es gab zwei Befehle, die direkt an die io_service gesendet wurden, während es timer1 handhabt.

Es scheint, dass in den Implementierungsdetails die Boost-Funktion den direkt aufgegebenen Aktionen Vorrang gegenüber den Timeraktionen gibt.

0

Der erste Timer-Ablauf blockiert den io (main) -Thread, während der andere Thread einige Elemente in der asio-Arbeitswarteschlange ablegt, sobald der Rückruf von Timer 1 abgeschlossen ist, wird der Ablauf des zweiten Timers verarbeitet Rückruf wird in die Warteschlange gestellt, aber nicht ausgeführt. da "3" & "4" wo bereits in der Warteschlange war (während "1" den Haupt-Thread blockierte), gehen sie vor "2"

Der Punkt von Asio ist nicht zu blockieren.Indem Sie lange laufende Arbeit im Callback der ersten Timer (der Ruhezustand) machen, haben Sie verhindert, dass der io-Thread rechtzeitig läuft. Sie sollten diese Arbeit in einen dedizierten Thread abladen und dessen Fertigstellung zurück an asio senden.

0

Die io_service übernimmt keine Garantie für die Aufrufreihenfolge der Handler. Theoretisch könnten die Handler in irgendeiner Reihenfolge aufgerufen werden, wobei einige Permutationen signifikant unwahrscheinlich sind.

Wenn Handler in einer bestimmten Reihenfolge aufgerufen werden müssen, sollten Sie die asynchronen Aufrufketten so umstrukturieren, dass die gewünschte Handler-Kette erzwungen wird. Zusätzlich kann es notwendig sein, die garantierte Reihenfolge des Handler-Aufrufs zu verwenden, die strand bereitstellt. Versuchen Sie nicht, komplexe Handleraufrufe durch spröde Schläge und Timer zu steuern.

+0

** ⚠ ** Ohne die Aufrufketten Nacharbeiten oder die Timer ändern, eine äußerst fragile Lösung, die stark auf Details der Implementierung abhängt ist verfügbar [hier] (http://coliru.stacked-crooked.com/a/0524433bb0bdcf71) ** ⚠ ** –

0

Ihr erstes Problem ist, dass Sie sich in einem Handler zu blockieren sind versuchen:

timer1.expires_from_now(boost::posix_time::seconds(1)); 
timer1.async_wait([](const boost::system::error_code& error) { 
    boost::this_thread::sleep(boost::posix_time::seconds(5)); // <--- HERE 
    printf("1 "); 
}); 

Was im obigen Code geschieht, nachdem timer1 wartet eine Sekunde lang ist, dass es den Rückruf an die io_service postet. Innerhalb der io_service::run Funktion wird dieser Callback ausgeführt, aber diese Ausführung findet innerhalb des Hauptthreads statt, so dass er für fünf Sekunden anhält, wodurch verhindert wird, dass timer2 seinen Handler zur Ausführung in io_service posten kann. Dies geschieht bis zur sechsten Sekunde der Programmausführung (6 = 5 + 1).

Inzwischen wird der Thread t ausgeführt und in der fünften Sekunde der Programmausführung werden diese beiden printf ("3") und printf ("4") an io_service gesendet.

boost::thread t([&io_service]() { 
    boost::this_thread::sleep(boost::posix_time::seconds(5)); 
    io_service.post([]() { 
     printf("3 "); 
    }); 
    io_service.post([]() { 
     printf("4 "); 
    }); 
}); 

Sobald die Prozedur von timer1 deblockiert, ermöglicht es timer2 seine Handler io_service zu posten. Dies geschieht wiederum in der sechsten Sekunde der Programmausführung, dh nachdem die printf("3") und printf("4") bereits gepostet wurden!

Alles in allem glaube ich, für das, was Sie suchen ist dies:

#include "boost/asio.hpp" 
#include "boost/thread.hpp" 

int main() 
{ 
    boost::asio::io_service io_service; 
    boost::optional<boost::asio::io_service::work> work(io_service); 

    boost::asio::deadline_timer timer1(io_service); 
    boost::asio::deadline_timer timer2(io_service); 

    timer1.expires_from_now(boost::posix_time::seconds(1)); 
    timer1.async_wait([](const boost::system::error_code& error) { 
     printf("1 "); 
    }); 

    timer2.expires_from_now(boost::posix_time::seconds(2)); 
    timer2.async_wait([](const boost::system::error_code& error) { 
     printf("2 "); 
    }); 

    boost::thread t([&io_service, &work]() { 
     boost::this_thread::sleep(boost::posix_time::seconds(5)); 
     io_service.post([]() { 
      printf("3 "); 
     }); 
     io_service.post([&work]() { 
      printf("4 "); 
      work = boost::none; 
     }); 
    }); 

    io_service.run(); 
    t.join(); 

    return 0; 
} 
Verwandte Themen