2009-06-16 11 views
27

C'è un modo per integrare Boost.Asio con Qt4 (preferito) o ciclo principale GTK? GTK fornisce sondaggio (2) come API, quindi tecnicamente dovrebbe essere possibile. Qt fornisce il proprio livello di rete, tuttavia preferisco usare il codice esistente scritto per Boost.Asio. Voglio integrarli senza utilizzando un thread aggiuntivo.Come integrare il loop principale di Boost.Asio nella struttura della GUI come Qt4 o GTK

C'è qualche riferimento su come farlo per Qt4 (preferito) o GTKmm?

Grazie.

Modifica

voglio clearify diverse cose per rendere la risposta più facile. Sia Qt e GTKmm forniscono "selezionare come" funzionalità:

Quindi, la domanda è: come integrare esistenti "selettori/poller", come reattore per Boost. Asio io_service. Oggi, Boost.Asio può utilizzare select, kqueue, epoll,/dev/poll e iocp come servizio reattore/proattore. Voglio integrarlo al ciclo principale del framework GUI.

Eventuali suggerimenti e soluzioni (migliori) sono i benvenuti.

+0

Qualche aggiornamento su una buona soluzione qui? Ho appena raggiunto lo stesso problema ... – Macke

risposta

8

È una domanda piuttosto vecchia, ma per coloro che stanno leggendo ora mi piacerebbe condividere my code che è un'implementazione di QAbstractEventDispatcher per boost :: asio.

Tutto ciò che serve è aggiungere la seguente riga prima di creare QApplication (di solito è in main()).

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

Esso provocherà, che io_service è gestito insieme con l'applicazione qt in un thread senza ulteriore latenza e calo di prestazioni (come nella soluzione chiamata io_service :: poll() "di volta in volta").

Sfortunatamente, la mia soluzione è solo per sistemi posix, poiché usa asio :: posix :: stream_descriptor. Il supporto di Windows potrebbe richiedere un approccio completamente diverso o abbastanza simile - non lo so davvero.

+0

Nice ... Grazie per aver condiviso. E non penso che non puoi farlo per Windows perché Windows ha un approccio molto diverso. Anche asio non supporta in realtà le operazioni in stile reattore su Windows - ricade su "seleziona" su thread diversi perché IOCP non supporta operazioni di tipo "select". – Artyom

+2

Sfortunatamente, gli sviluppatori qt non consigliano una soluzione del genere: http://www.qtcentre.org/threads/53229-Come-integrare-asio-io_service-with-Qt-event-loop. – peper0

+0

@ peper0 Stai dicendo che gli sviluppatori di Qt non consigliano una soluzione come quella che hai implementato qui? Non lo vedo abbastanza dal thread che hai collegato a ... –

6

Se ho capito bene la tua domanda, hai codice scritto per Boost.Asio. Vorresti usare quel codice all'interno di un'applicazione GUI.

Ciò che non è chiaro nella tua domanda è se vuoi avvolgere i livelli di rete Qt/Gtk attraverso asynio per far funzionare il tuo codice, se stai solo cercando una soluzione per avere insieme un ciclo di eventi gui e asynio.

Assumerò il secondo caso.

Sia Qt che Gtk hanno metodi per integrare eventi stranieri nel loro ciclo di eventi. Si veda ad esempio qtgtk dove il loop di eventi Qt è collegato a Gtk.

Nel caso specifico di Qt, se si desidera generare eventi per Qt, è possibile utilizzare la seguente classe: QAbstractEventDispatcher.

Dopo un rapido sguardo a spinta asio, penso che è necessario effettuare le seguenti operazioni:

  • hanno un QTimer ricorrente con durata nulla che chiama io_service :: run() per tutto il tempo. In questo modo, boost :: asio chiamerà il gestore di completamento non appena viene completata l'operazione asincrona.
  • nel vostro gestore di completamento, due opzioni:
    • se l'operazione di completamento è lunga, separata dalla GUI, fare il vostro business e assicurarsi di chiamare QAPP.processEvents() regolarmente per mantenere l'interfaccia grafica sensibile
    • se si vuole solo comunicare di nuovo con la GUI:
      1. definire un QEvent tipo personalizzato
      2. subscribe to this event
      3. Pubblica il tuo evento a Qt ciclo di eventi utilizzando QCoreApplication::postEvent().
+2

La soluzione che suggerisci richiede qualche tipo di attesa di timeout occupato o breve, che non è ottimale. Preferisco unire sia il ciclo pari che non il run-qt/run-asio/run-qt/run-asio, specialmente quando aspetto uno dei seguenti: input/input dell'utente dalla rete che può avvenire simultaneamente. – Artyom

+1

Non c'è nessun pranzo gratis. Se ho capito bene, asio ha una parte essenziale che chiama la funzione :: run(), che sta bloccando. Se quella funzione sta bloccando, o la chiami da un thread in background, o potrebbe bloccare la tua applicazione. Si menziona l'integrazione del ciclo di eventi ma dalla ricerca rapida, non ho trovato alcun ciclo di eventi in asio. Le chiamate asincrone non vanno necessariamente con un ciclo di eventi. –

+0

Puoi anche farlo al contrario: usa Qt per tutto, e solo "converti" eventi di rete qt in un formato previsto da asio. In questo modo, non si userebbe asio, ma si dovrebbe chiamare il codice legacy nello stesso asio usato per fare. –

15

Semplice: Costruire uno slot QT che chiama il io_service::poll_one() appartenente alla gui. Collegare lo slot al segnale tick di QT.

In profondità: Fortunatamente per te Boost.Asio è molto ben progettato. Esistono molte opzioni su come fornire un thread di esecuzione agli interni asincroni sottostanti. Le persone hanno già menzionato l'uso di io_service::run(), una chiamata bloccante con molti svantaggi.

È possibile accedere solo ai widget GUI da una singola discussione. I thread esterni generalmente hanno bisogno di inviare eventi alla GUI se vogliono fare la modifica di qualsiasi widget. Questo è molto simile a come funziona Asio.

L'approccio ingenuo è dedicare solo un thread (o un timer) a eseguire io_service::run() e il gestore di completamento Asio postare un segnale di GUI. Questo sarà lavoro.

Invece è possibile utilizzare la garanzia che i gestori di completamento vengano chiamati solo nella sequenza di esecuzione del chiamante io_service. Non fare in modo che il thread del GUI chiami io_service::run() mentre blocca e potrebbe bloccare la GUI. Utilizzare invece io_service::poll() o io_service::poll_one(). Ciò causerà la chiamata di tutti i gestori di completamento Asio in attesa dal thread GUI. Poiché i gestori sono in esecuzione nel thread GUI, sono liberi di modificare i widget.

Ora è necessario assicurarsi che l'io_service abbia la possibilità di funzionare regolarmente. Raccomando di avere un segnale gui ripetuto chiamare lo poll_one() un paio di volte. Credo che QT abbia un segnale di spunta che farebbe il trucco. Ovviamente potresti trasmettere il tuo segnale QT per un maggiore controllo.

+3

Che segnale "tick"? Sembra che ASIO farebbe delle attese molto impegnative che porteranno a un elevato utilizzo della CPU. – cheez

+1

È passato un po 'di tempo ma sembra che il segnale di spunta sia univoco per PyQT4. Per una soluzione C++, un 'QTimer' funzionerebbe, o qualsiasi altro segnale che spara regolarmente. –

+0

@cheez, la ragione per chiamare 'poll_one' invece di' poll' è di evitare lunghe pause che potrebbero verificarsi se si verificano molti eventi di rete. Suggerisco la semplice soluzione di chiamare 'poll_one' alcune volte per evitare il problema dove gli eventi di rete sono più frequenti di "poll_one". Una ovvia ottimizzazione è quella di uscire anticipatamente dal ciclo se "poll_one" restituisce 0 (evita il ciclo occupato). Un'altra soluzione più complessa sarebbe quella di eseguire il loop su 'poll_one' finché 0 non viene restituito o viene superata una soglia temporale (ad esempio 100ms). –

2

Integrando genuinamente i loop principali è possibile. È solo un grande dolore (e devo ancora provarlo davvero).

L'esecuzione di io_service :: run() su una thread separata è probabilmente la strada da percorrere.