2009-06-16 12 views
27

Czy istnieje sposób na integrację Boost.Asio z Qt4 (preferowaną) lub główną pętlą GTK? GTK zapewnia ankietę (2) podobną do API, więc technicznie powinna być możliwa. Qt zapewnia własną warstwę sieciową, jednak wolę używać istniejącego kodu napisanego dla Boost.Asio. Chcę je zintegrować bez za pomocą dodatkowego wątku.Jak zintegrować główną pętlę Boost.Asio w strukturze GUI, jak Qt4 lub GTK

Czy jest jakieś odniesienie, jak to zrobić dla Qt4 (preferowane) lub GTKmm?

Dzięki.

Edit

Chcę clearify kilka rzeczy, aby odpowiedź łatwiejsze. Zarówno Qt i gtkmm zapewnić "wybierz jak" funkcjonalność:

Więc pytanie brzmi, jak zintegrować istniejące "selektorów/pollers" jako reaktor do impuls. Asio io_service. Obecnie Boost.Asio może używać select, kqueue, epoll,/dev/poll i iocp jako usługi reaktora/proaktora. Chcę go zintegrować z główną pętlą interfejsu GUI.

Wszelkie sugestie i rozwiązania (lepsze) są mile widziane.

+0

Każda aktualizacja dobrego rozwiązania tutaj? Właśnie trafiłem na ten sam problem ... – Macke

Odpowiedz

8

To raczej stare pytanie, ale dla tych, którzy teraz go czytają, chciałbym udostępnić my code, która jest implementacją QAbstractEventDispatcher dla boost :: asio.

Wystarczy dodać następujący wiersz przed utworzeniem QApplication (zwykle jest to w main()).

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

Spowoduje to, że io_service prowadzony jest wspólnie z qt aplikacji w jednym wątku bez dodatkowej latencji i spadku wydajności (jak w roztworze z wywołaniem io_service :: Sonda() „od czasu do czasu”).

Niestety, moje rozwiązanie dotyczy tylko systemów Posix, ponieważ używa asio :: posix :: stream_descriptor. Obsługa Windows może wymagać zupełnie innego podejścia lub całkiem podobnego - tak naprawdę nie wiem.

+0

Miło ... Dziękuję za udostępnienie. I nie sądzę, że nie możesz tego zrobić dla Windows, ponieważ okna mają bardzo różne podejście. Asio tak naprawdę nie obsługuje operacji w stylu reaktora w systemie Windows - wraca do "wyboru" w innym wątku, ponieważ IOCP nie obsługuje operacji "wybierz" jak. – Artyom

+2

Niestety, programiści qt nie zalecają takiego rozwiązania: http://www.qtcentre.org/threads/53229-How-to-integrate-asio-io_service-with-Qt-event-loop. – peper0

+0

@ peper0 Czy mówisz, że programiści Qt nie zalecają takiego rozwiązania, jak tutaj? Nie całkiem widzę to z wątku, z którym się łączyłeś ... –

6

Jeśli dobrze rozumiem twoje pytanie, masz kod napisany dla Boost.Asio. Chciałbyś użyć tego kodu w aplikacji GUI.

Nie jest jasne w twoim pytaniu, czy chcesz owijać warstwy sieciowe Qt/Gtk przez asynio, aby twój kod zadziałał, jeśli szukasz rozwiązania, które zapewnia zarówno pętlę zdarzeń GUI, jak i Asynio.

Przyjmę drugi przypadek.

Zarówno Qt, jak i Gtk mają metody integracji zdarzeń obcych w pętli zdarzeń. Zobacz na przykład qtgtk, gdzie pętla zdarzeń Qt jest podłączona do Gtk.

W konkretnym przypadku Qt, jeśli chcesz generować zdarzenia dla Qt, możesz użyć następującej klasy: QAbstractEventDispatcher.

Po szybkim spojrzeniem na impuls ASIO, myślę, że trzeba wykonać następujące czynności:

  • mają powtarzające QTimer z zerowym czasie trwania, który wywołuje io_service :: run() cały czas. W ten sposób, boost :: asio wywoła twój program obsługi zakończenia, jak tylko zakończy się twoja operacja asynchroniczna.
  • w swojej obsługi realizacji, dwie opcje:
    • jeśli operacja ukończenie jest długa, oddzielona od GUI, czy firmę i upewnij się, aby zadzwonić qApp.processEvents() regularnie, aby zachować GUI czułe
    • jeśli chcesz tylko do komunikowania się z powrotem z GUI:
      1. zdefiniować niestandardowy QEvent typ
      2. subscribe to this event
      3. dodaj swoje wydarzenie do pętli zdarzeń Qt przy użyciu QCoreApplication::postEvent().
+2

Proponowane przez ciebie rozwiązanie wymaga jakiegoś zajętego lub krótkiego czasu oczekiwania, który jest suboptymalny. Raczej chcę scalić obie pętle, a nie uruchomić qt/run-asio/run-qt/run-asio, szczególnie gdy czekam na jedno z: danych wejściowych/wejściowych użytkownika z sieci, które mogą występować jednocześnie. – Artyom

+1

Nie ma darmowego lunchu. Jeśli rozumiem poprawnie, asio ma istotną część, która wywołuje funkcję :: run(), która jest blokowana. Jeśli ta funkcja jest blokowana, możesz wywołać ją z wątku tła lub zablokować aplikację. Wspominasz o integracji pętli zdarzeń, ale z szybkiego przeglądania nie znalazłem żadnej pętli zdarzeń w asio. Wywoływanie asynchroniczne niekoniecznie wiąże się z pętlą zdarzeń. –

+0

Możesz też zrobić to w drugą stronę: użyj Qt na wszystko i po prostu "przekonwertuj" zdarzenia sieciowe qt na format oczekiwany przez asio. W ten sposób nie używałbyś Asio, ale musiałbyś wywołać starszy kod w tym samym, co wcześniej używane. –

15

Proste: zbudować gniazdo QT, który wywołuje io_service::poll_one() należące do GUI. Podłącz to gniazdo do sygnału QT o numerze tick.

Głębokość: Na szczęście dla Ciebie Boost.Asio jest bardzo dobrze zaprojektowany. Istnieje wiele opcji dostarczania wątku wykonywania do podstawowych asynchronicznych elementów wewnętrznych. Ludzie już wspomnieli o korzystaniu z io_service::run(), połączenia blokującego z wieloma wadami.

Dostęp do widgetów GUI można uzyskać tylko z jednego wątku. Zewnętrzne wątki zazwyczaj muszą publikować zdarzenia w gui, jeśli chcą zmutować dowolny widget. Jest to bardzo podobne do działania Asio.

Podejście naiwne polega na poświęceniu tylko jednego wątku (lub timera) na uruchomienie io_service::run(), a obsługa zakończenia Asio po sygnale GUI. To będzie działać pod numerem .

Zamiast tego można użyć gwarancji, że procedury obsługi zakończenia będą wywoływane tylko w wątku wykonywania wywołującego io_service. Nie ma wywoływania wątku GUI io_service::run(), ponieważ jest on blokowany i może zawiesić GUI. Zamiast tego użyj io_service::poll() lub io_service::poll_one(). Spowoduje to wywołanie wszystkich oczekujących programów obsługi zakończenia Asio z wątku GUI. Ponieważ moduły obsługi działają w wątku GUI, mogą modyfikować widżety.

Teraz musisz się upewnić, że io_service ma szansę na regularne bieganie. Polecam kilka razy powtarzające się wywołanie sygnału gui poll_one(). Wierzę, że QT ma sygnał kleszcza, który mógłby wystarczyć. Można oczywiście rzucić własny sygnał QT, aby uzyskać większą kontrolę.

+3

Co oznacza "tick"? Wygląda na to, że ASIO wykona zapracowane oczekiwania, które doprowadzą do dużego obciążenia procesora. – cheez

+1

Minęło trochę czasu, ale wygląda na to, że sygnał zaznaczenia jest unikalny dla PyQT4. Dla rozwiązania C++ działałoby 'QTimer' lub jakikolwiek inny sygnał, który regularnie odpala. –

+0

@cheez, powodem wywołania 'poll_one' zamiast' poll' jest uniknięcie długich pauz, które mogą wystąpić, jeśli pojawi się wiele zdarzeń sieciowych. Proponuję proste rozwiązanie wywoływania 'poll_one' kilka razy, aby uniknąć problemu gdzie zdarzenia sieciowe są częstsze niż wywołanie 'poll_one'. Oczywistą optymalizacją jest wczesne wyjście z pętli, jeśli 'poll_one' zwróci 0 (uniknij zajętej pętli). Innym bardziej złożonym rozwiązaniem byłoby pętla 'poll_one' do momentu powrotu 0 lub przekroczenia progu czasu (powiedzmy 100ms). –

2

Rzeczywiście możliwa jest integracja głównych pętli z. To po prostu wielki ból (i jeszcze go nie wypróbowałem).

Uruchamianie io_service :: run() w osobnym wątku jest prawdopodobnie drogą do zrobienia.