How to integrate Boost.Asio main loop in GUI frame

2019-01-16 09:28发布

问题:

Is there any way to integrate Boost.Asio with Qt4 (preferred) or GTK main loop? GTK provides poll(2) like API so technically is should be possible. Qt provides its own networking layer, however I prefer to use existing code written for Boost.Asio. I want to integrate them without using an additional thread.

Is there any reference how to do this for Qt4 (preferred) or GTKmm?

Thanks.

Edit

I want to clearify several things to make the answer easier. Both Qt and GTKmm provide "select like" functionality:

  • http://qt-project.org/doc/qt-5.0/qtcore/qsocketnotifier.html
  • http://www.gtkmm.org/docs/glibmm-2.4/docs/reference/html/group__MainLoop.html

So, the question is, how to integrate existing "selectors/pollers" as reactor to Boost.Asio io_service. Today, Boost.Asio can use select, kqueue, epoll, /dev/poll and iocp as reactor/proactor service. I want to integrate it to the main-loop of GUI framework.

Any suggestions and solutions (better) are welcome.

回答1:

It's rather old question but for those who are reading it now I would like to share my code which is an implementation of QAbstractEventDispatcher for boost::asio.

All you need is to add the following line before creating QApplication (usually it's in main()).

QApplication::setEventDispatcher(new QAsioEventDispatcher(my_io_service));

It will cause, that io_service is being run together with qt application in one thread without additional latency and performance drop (like in solution with calling io_service::poll() "from time to time").

Unfortunately, my solution is for posix systems only, since it use asio::posix::stream_descriptor . Windows support may need completely different approach or quite similar - I don't really know.



回答2:

Simple: Build a QT slot that calls the io_service::poll_one() belonging to the gui. Connect that slot to QT's tick signal.

In Depth: Luckily for you Boost.Asio is very well designed. There are many options on how to provide a thread of execution to the underlying asynchronous internals. People have already mention using io_service::run(), a blocking call with many disadvantages.

You are only allowed to access gui widgets from a single thread. External threads generally need to post events to the gui if they want to do mutate any widget. This is very similar to how Asio works.

The naive approach is to just dedicate one thread (or timer) to running io_service::run() and have the Asio completion handler post a gui signal. This will work.

Instead you can use the guarantee that completion handlers will only be called in the thread of execution of the io_service caller. Don't have the gui thread call io_service::run() as it is blocking and could hang the gui. Instead use io_service::poll() or io_service::poll_one(). This will cause any pending Asio completion handlers to be called from the gui thread. Since the handlers are running in the gui thread they are free to modify the widgets.

Now you need make sure the io_service gets a chance to run regularly. I recommend having a repeating gui signal call poll_one() a few times. I believe QT has a tick signal that would do the trick. You could of course roll your own QT signal for more control.



回答3:

If I understand your question correctly, you have code written for Boost.Asio . You would like to use that code inside a GUI application.

What is not clear in your question is if you want to wrap the Qt/Gtk network layers through asynio for your code to work, if you are just looking for a solution for having both a gui event loop and asynio together.

I will assume the second case.

Both Qt and Gtk have methods to integrate foreign events in their event loop. See for example qtgtk where the Qt event loop is plugged into Gtk.

In the specific case of Qt, if you want to generate events for Qt, you can use the following class: QAbstractEventDispatcher.

After a quick look at boost asio, I think you need to do the following:

  • have a recurring QTimer with duration zero that calls io_service::run() all the time. That way, boost::asio will call your completion handler as soon as your asynchronous operation is completed.
  • in your completion handler, two options:
    • if your completion operation is a long one, separated from the GUI, do your business and make sure to call qApp.processEvents() regularly to keep the GUI responsive
    • if you just want to communicate back with the gui:
      1. define a custom QEvent type
      2. subscribe to this event
      3. post your event to Qt event loop using QCoreApplication::postEvent().


回答4:

Genuinely integrating the main loops is possible. It's just a big pain (and I have yet to actually try it).

Running io_service::run() on a separate thread is probably the way to go.