Как интегрировать Boost.Основной цикл Asio в GUI framework, такой как Qt4 или GTK
-
05-07-2019 - |
Вопрос
Есть ли какой-либо способ интегрировать Boost.Asio с Qt4 (предпочтительно) или GTK main loop?GTK предоставляет poll (2), подобный API, так что технически это должно быть возможно.Qt предоставляет свой собственный сетевой уровень, однако я предпочитаю использовать существующий код, написанный для Boost.Asio.Я хочу объединить их без используя дополнительный поток.
Есть ли какая-нибудь ссылка, как это сделать для Qt4 (предпочтительно) или GTKmm?
Спасибо.
Редактировать
Я хочу прояснить несколько вещей, чтобы упростить ответ.И Qt, и GTKmm предоставляют функциональность "выбрать понравившееся":
- 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
Итак, вопрос в том, как интегрировать существующие "селекторы / опросчики" в качестве реактора для
Boost.Asio io_service
.Сегодня запускаем Boost.Asio может использовать select, kqueue, epoll, /dev/poll и iocp в качестве службы reactor / proactor.Я хочу интегрировать его в основной цикл GUI framework.
Любые предложения и решения (лучше) приветствуются.
Решение
Это довольно старый вопрос, но для тех, кто читает его сейчас, я хотел бы поделиться мой код который является реализацией QAbstractEventDispatcher для boost::asio.
Все, что вам нужно, это добавить следующую строку перед созданием QApplication (обычно это в main()).
QApplication::setEventDispatcher(new QAsioEventDispatcher(my_io_service));
Это приведет к тому, что io_service будет запускаться вместе с приложением qt в одном потоке без дополнительной задержки и снижения производительности (как в решении с вызовом io_service::poll() "время от времени").
К сожалению, мое решение предназначено только для систем posix, поскольку оно использует asio::posix::stream_descriptor .Службе поддержки Windows может потребоваться совершенно другой подход или очень похожий - я действительно не знаю.
Другие советы
Простой:
Создайте слот QT , который вызывает io_service::poll_one()
принадлежащий графическому интерфейсу.Подключите этот слот к QT's tick
сигнал.
В Глубине:
К счастью для вас, Boost.Asio очень хорошо разработан.Существует много вариантов того, как предоставить поток выполнения базовым асинхронным внутренним устройствам.Люди уже упоминали об использовании io_service::run()
, блокирующий вызов со многими недостатками.
Вам разрешен доступ к виджетам gui только из одного потока.Внешним потокам обычно необходимо публиковать события в графическом интерфейсе, если они хотят изменить какой-либо виджет.Это очень похоже на то, как работает Asio.
Наивный подход заключается в том, чтобы просто выделить один поток (или таймер) для запуска io_service::run()
и пусть обработчик завершения Asio отправит сигнал gui.Это будет работать.
Вместо этого вы можете использовать гарантию того, что обработчики завершения будут вызываться только в потоке выполнения io_service
звонивший.У вас нет вызова потока gui io_service::run()
так как это блокирует и может привести к зависанию графического интерфейса.Вместо этого используйте io_service::poll()
или io_service::poll_one()
.Это приведет к вызову всех ожидающих завершения обработчиков Asio из потока gui.Поскольку обработчики выполняются в потоке gui, они могут свободно изменять виджеты.
Теперь вам нужно убедиться, что io_service
получает возможность регулярно бегать.Я рекомендую использовать повторяющийся вызов сигнала gui poll_one()
несколько раз.Я считаю, что у QT есть тиковый сигнал, который бы сделал свое дело.Конечно, вы могли бы создать свой собственный QT-сигнал для большего контроля.
Если я правильно понял ваш вопрос, у вас есть код, написанный для Boost.Asio .Вы хотели бы использовать этот код внутри приложения с графическим интерфейсом.
Что неясно в вашем вопросе, так это то, хотите ли вы обернуть сетевые уровни Qt / Gtk через asynio, чтобы ваш код работал, если вы просто ищете решение для совместного использования цикла событий gui и asynio.
Я возьму на себя второй случай.
И Qt, и Gtk имеют методы для интеграции внешних событий в свой цикл событий.Смотрите, например qtgtk ( ктгтк ) где цикл событий Qt подключен к Gtk.
В конкретном случае Qt, если вы хотите генерировать события для Qt, вы можете использовать следующий класс: QAbstractEventDispatcher.
После беглого ознакомления с boost asio, я думаю, вам нужно сделать следующее:
- имейте повторяющийся QTimer с нулевой длительностью, который постоянно вызывает io_service::run().Таким образом, boost::asio вызовет ваш обработчик завершения, как только ваша асинхронная операция будет завершена.
- в вашем обработчике завершения есть два варианта:
- если ваша операция завершения длинная и отделена от графического интерфейса, занимайтесь своими делами и обязательно регулярно вызывайте qApp.processEvents(), чтобы графический интерфейс оставался отзывчивым
- если вы просто хотите связаться с графическим интерфейсом:
- определите пользовательский Событие Тип
- подпишитесь на это событие
- разместите свое событие в цикле событий Qt, используя QCoreApplication::postEvent() QCoreApplication::postEvent().
Подлинная интеграция основных циклов является возможно.Это просто большая боль (и мне еще предстоит это попробовать).
Вероятно, лучше всего запустить io_service::run() в отдельном потоке.