Synchronisation mit boost::asio::strand
-
Hallo,
ich habe gerade etwas mit
boost::asio
herumgespielt und einen TCP/IP Server gebastelt. Ich stehe jetzt vor dem Problem, dass ich Funktionsaufrufe in dem Thread machen möchte, der den TCP/IP Server erstellt hat (Hauptthread der Anwendung). Dasboost::asio::io_service
Objekt wird in einem eigenen Thread bedient, damit die Anwendung nicht blockiert wird:#include <thread> #include <boost/asio.hpp> class TCPIPServer { std::thread ServiceThread_; boost::asio::io_service Service_; boost::asio::ip::tcp::socket Socket_; boost::asio::ip::tcp::acceptor Acceptor_; public: TCPIPServer() : Socket_( Service_ ), Acceptor_( Service_ ) { } void start() { ... ServiceThread_ = std::thread( service_driver ); ... } void stop() { ... Service_.stop(); ServiceThread_.join(); Service_.reset(); } void service_driver() { ... async_accept(); Service_.run(); ... } void async_accept() { Acceptor_.async_accept( Socket_, [this]( const boost::system::error_code& ec ) { if( !ec ) { // Thread Kontext des Driver Threads on_connect( /* ... */ ); async_accept(); } } ); } void on_connect( /*...*/ ) { // hier möchte ich in den Hauptthread wechseln, um alle Listener über den // Verbindungsaufbau zu informieren. Listeners_( /* ... */ ); } };
Sobald sich ein Client verbindet wird der Code ab Zeile 42 ausgeführt, der vom Driver Thread des io_service bedient wird. Da mein GUI Framework Aktualisierungen nur aus dem Anwendungs-Hauptthread zulässt möchte ich ab Zeile 56 wieder in den Hauptthread springen, um dort alle Listener im Hauptthread der Anwendung zu informieren.
Mit
boost::asio::strand
können Aufrufe zwischen zweiio_service
Objekten synchronisiert werden, die von unterschiedlichen Threads bedient werden. Das Problem dabei ist jetzt auch wieder, wie ich ein zweitesio_service
Objekt aus dem Hauptthread bediene, denn derrun()
Aufruf muss ja aus dem Hauptthread erfolgen. Die Konstruktion eines zweitenio_service
Objekts mit zugehörigemboost::asio::strand
Objekt reicht da nicht aus.Unter Windows kann man sich ein Konstrukt mit einem Synchronisationsfenster und
PostMessage
basteln, aber ich frage mich, ob das nicht auch überboost::asio
realisiert werden kann. Hat da jemand eine Idee?
-
Kurz: Nein, das geht leider nicht.
Lang:
Das geht nur dann mitboost.asio
, wenn dein Hauptthread auch mit einemboost::asio::io_service
läuft und genau ein Threadrun(..)
ausführt. Du hättest dann einboost::asio::io_service
mit zugehörigen Threads für den Server und einboost::asio::io_service
für den Hauptthread, der dein GUI Loop ausführt. Dann können die Aufrufe vom Server-Teil mittelspost(..)
auf den Hauptthread gemarshallt werden.Nun ist es vermutlich nicht ganz einfach, den
boost::asio::io_service
mit deinem GUI Loop zu "verheiraten" (falls es überhaupt sinnvoll geht). Deshalb ist es wohl besser den Server-Teil über den zum GUI Loop passenden Mechanismus auf den Hauptthread zu marshallen. Ich habe das jeweils so gemacht, dass den Marshalling-Mechanismus (unter WindowsQueueUserAPC(..)
/PostMessage(..)
/PostThreadMessage(..)
...) in einem Dispatcher-Objekt gekapselt wird. Über ein Interface/Template kann man es austauschbar machen, je nach GUI Loop.