2009-06-16 15 views
27

¿Hay alguna manera de integrar Boost.Asio con Qt4 (preferido) o bucle principal de GTK? GTK proporciona poll (2) como API por lo que técnicamente debería ser posible. Qt proporciona su propia capa de red, sin embargo, prefiero usar el código existente escrito para Boost.Asio. Quiero integrarlos sin usando una rosca adicional.Cómo integrar el bucle principal de Boost.Asio en el marco de GUI como Qt4 o GTK

¿Hay alguna referencia sobre cómo hacer esto para Qt4 (preferido) o GTKmm?

Gracias.

Editar

Quiero clearify varias cosas para hacer más fácil la respuesta. Tanto Qt y GTKmm proporcionan "seleccionar como" funcionalidad:

Entonces, la pregunta es, cómo integrar "selectores/pollers" existentes como reactor para Boost. Asio io_service. Hoy, Boost.Asio puede usar select, kqueue, epoll,/dev/poll y iocp como reactor/proactor service. Quiero integrarlo en el bucle principal del marco de GUI.

Cualquier sugerencia y solución (mejor) son bienvenidas.

+0

¿Alguna actualización de una buena solución aquí? Acabo de abordar el mismo problema ... – Macke

Respuesta

8

Es una pregunta bastante antigua, pero para aquellos que la están leyendo ahora me gustaría compartir my code que es una implementación de QAbstractEventDispatcher para boost :: asio.

Todo lo que necesita es agregar la siguiente línea antes de crear QApplication (generalmente está en main()).

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

Esto causará que io_service se ejecuta junto con la aplicación del intervalo QT en un hilo sin latencia adicional y la caída de rendimiento (como en solución con llamar io_service :: poll() "de vez en cuando").

Desafortunadamente, mi solución es solo para sistemas posix, ya que usa asio :: posix :: stream_descriptor. El soporte de Windows puede necesitar un enfoque completamente diferente o bastante similar, realmente no lo sé.

+0

Agradable ... Gracias por compartir. Y no creo que no puedas hacerlo para Windows porque Windows tiene un enfoque muy diferente. También asio no es compatible con las operaciones de estilo de reactores en Windows; vuelve a "seleccionar" en subprocesos diferentes porque IOCP no admite operaciones como "seleccionar". – Artyom

+2

Desafortunadamente, los desarrolladores de qt no recomiendan tal solución: http://www.qtcentre.org/threads/53229-How-to-integrate-asio-io_service-with-Qt-event-loop. – peper0

+0

@ peper0 ¿Está diciendo que los desarrolladores de Qt no recomiendan una solución como la que ha implementado aquí? No estoy viendo exactamente eso desde el hilo que vinculó a ... –

6

Si entiendo su pregunta correctamente, tiene un código escrito para Boost.Asio. Le gustaría usar ese código dentro de una aplicación GUI.

Lo que no queda claro en su pregunta es si desea ajustar las capas de red Qt/Gtk mediante asynio para que funcione su código, si solo está buscando una solución para tener un evento gui y asynio juntos.

Voy a asumir el segundo caso.

Tanto Qt como Gtk tienen métodos para integrar eventos externos en su ciclo de eventos. Ver por ejemplo qtgtk donde el bucle de evento Qt está conectado a Gtk.

En el caso específico de Qt, si desea generar eventos para Qt, puede usar la siguiente clase: QAbstractEventDispatcher.

Después de un rápido vistazo a impulso asio, creo que hay que hacer lo siguiente:

  • tienen una QTimer recurrentes con una duración de cero que io_service llama :: run() todo el tiempo. De esta forma, boost :: asio llamará a su controlador de finalización tan pronto como se complete su operación asincrónica.
  • en su manejador de finalización, dos opciones:
    • si su operación de finalización es largo, separado de la interfaz gráfica de usuario, hacer su negocio y asegúrese de llamar QAPP.processEvents() con regularidad para mantener la interfaz gráfica de usuario sensible
    • si lo que desea comunicarse de vuelta con la GUI:
      1. definen una costumbre QEvent tipo
      2. subscribe to this event
      3. publicar su evento al bucle de eventos Qt utilizando QCoreApplication::postEvent().
+2

La solución que sugiere requiere algún tipo de espera de tiempo de espera ocupado o corto, que no es óptima. Prefiero fusionar ambos, incluso en bucle y no ejecutar-qt/run-asio/run-qt/run-asio, especialmente cuando espero uno de: entrada/entrada de usuario de la red que puede ocurrir simultáneamente. – Artyom

+1

No hay almuerzo gratis. Si entendí correctamente, asio tiene una parte esencial que llama a la función :: run(), que está bloqueando. Si esa función está bloqueando, o la llamas desde un hilo de fondo o puede bloquear tu aplicación. Mencionas la integración del bucle de eventos, pero al navegar rápidamente, no he encontrado ningún bucle de eventos en asio. La llamada asincrónica no necesariamente va con un bucle de evento. –

+0

También puede hacerlo al revés: use Qt para todo y simplemente "convierta" eventos de red qt en un formato esperado por asio. De esta forma, no usaría asio, pero tendría que llamar al código heredado en el mismo asio que solía hacer. –

15

simple: Construir una ranura QT que llama a la io_service::poll_one() perteneciente a la interfaz gráfica de usuario. Conecte esa ranura a la señal de QT tick.

En profundidad: Afortunadamente para ti Boost.Asio está muy bien diseñado. Hay muchas opciones sobre cómo proporcionar un hilo de ejecución a las partes internas asincrónicas subyacentes. La gente ya mencionó usar io_service::run(), una llamada de bloqueo con muchas desventajas.

Solo se le permite acceder a widgets de GUI desde un único hilo. Por lo general, los hilos externos necesitan publicar eventos en la interfaz gráfica si quieren mutar algún widget. Esto es muy similar a cómo funciona Asio.

El enfoque ingenuo consiste en dedicar un hilo (o temporizador) a ejecutar io_service::run() y hacer que el controlador de finalización de Asio publique una señal gui. Este funcionará.

En su lugar, puede utilizar la garantía de que los manejadores de terminación solo serán llamados en el hilo de ejecución de la llamada io_service. No tenga el hilo gui llame al io_service::run() ya que está bloqueando y podría colgar la GUI. En su lugar, use io_service::poll() o io_service::poll_one(). Esto causará que cualquier manejador de finalización Asio pendiente sea llamado desde el hilo gui. Dado que los controladores se ejecutan en el hilo de la interfaz gráfica de usuario, pueden modificar los widgets.

Ahora necesita asegurarse de que el io_service tenga la oportunidad de funcionar regularmente. Recomiendo tener una señal de GUI repitiendo llamada poll_one() un par de veces. Creo que QT tiene una señal de tic que haría el truco. Por supuesto, puede lanzar su propia señal QT para tener más control.

+3

¿Qué señal de "tic"? Parece que esto haría que ASIO esté ocupado esperando, lo que conducirá a un uso intensivo de la CPU. – cheez

+1

Ha pasado un tiempo, pero parece que la señal de tick es exclusiva de PyQT4. Para una solución C++, un 'QTimer' funcionaría, o cualquier otra señal que se dispare regularmente. –

+0

@cheez, la razón para llamar 'poll_one' en lugar de' poll' es evitar largas pausas que podrían ocurrir si entran muchos eventos de red. Sugiero la solución simple de llamar 'poll_one' varias veces para evitar el problema donde los eventos de red son más frecuentes de lo que se llama a 'poll_one'. Una optimización obvia es salir temprano del ciclo si 'poll_one' devuelve 0 (evite el bucle ocupado). Otra solución más compleja sería repetir '' poll_one' hasta que 0 sea devuelto o se pase un umbral de tiempo (digamos 100ms). –

2

La integración genuina de los bucles principales es posible. Es solo un gran dolor (y todavía tengo que intentarlo).

Ejecutar io_service :: run() en un hilo separado es probablemente el camino a seguir.

Cuestiones relacionadas