Wykonanie funkcji współbieżnie, tak aby nie blokował się wątek

0

Witam, mam klasę w Qt, która wygląda tak:

class Board : public QWidget
{
    Q_OBJECT
public:
signals:

public slots:
     void start();
private:
     void toDo();
}

Chciałbym wykonać współbierznie funkcję toDo tak aby nie blokował mi się wątek. W tej funkcji znajduje się długa pętla, wykonująca dość zasobożerne obliczenia na różnych obiektach. NIestety mam problem z odpaleniem wątku z biblioteki Qt (QThread). Są dwie możliwości, albo dziedziczenie po QThread, implementacja metody run() i tam wciśnięcie tego kodu. Ale ja już dziedziczę po klasie QWidget więc odpada. Drugim jest takie wykorzystanie metody moveToThread ale ona nie działa. Wyrzuca jakiś błąd (nie będę go przytaczał bo ta metoda mnie nie interesuje). Skoro Qt nie dostarcza mi możliwości stworzenia wątku postanowiłem wykorzystać bibliotekę <thread>. Zrobiłem coś takiego:

void Board::start()
{
    std::thread th(&Board::run, this);
}

Program owszem się skompilował ale przy próbie wystartowania wątku program się wysypuje prawdopodobnie dlatego że nigdzie nie dałem .join(). (debugger pokazał że wykonał się warunek w bibliotece thread

if(joinable())
  std::terminate();

ale gdy dam th.join() to wątek główny czeka na wątek poboczny - a to chcę uniknąć bo blokuje mi interfejs.

Proszę o pomoc.
Pozdrawiam :)

2

Przenieś tą obszerną logikę do innej klasy, którą odpalisz w wybranym wątku. Jeśli masz w tej pętli dużo odniesień do GUI to je usuń. @MarekR22 ledwo co podlinkował fajny opis użycia klasy QThread:

MarekR22 napisał(a):

Z moim kodem, stosując mechanizm slotów i sygnałów, nie musisz stosować wątków. Na dodatek jeśli się uprzesz stosować wątki, to będzie to o wiele łatwiejsze.
Tu jest mój opis jak używać wątków w qt: http://4programmers.net/Forum/C_i_C++/210633-qtcreator-_watki?p=918099#id918099
A tu ten z nowszej dokumentacji: http://qt-project.org/doc/qt-5.0/qtcore/qthread.html#details

http://blog.qt.digia.com/blog/2010/06/17/youre-doing-it-wrong/

0

Dzięki, to pomogło, choć wolałem tego uniknąć ale chyba nie ma wyjścia. Jeszcze jedno pytanie. Chcę aby moja klasa, która jest gdzieś tam głęboko i nie ma dostępu do GUI miała możliwość zmiany wartości w QLabel (coś się dzieje w tej klasie i ona aktualizuje go). Trzeba jakoś przekazać wskaźnik do tej klasy lub gdzieś go przechwywać globalnie. Jak to najwydajniej zrobić?

0

Najwydajniej? Przekazać wskaźnik do tego obiektu QLabel i zmienić jego wartość kiedy chcesz (pod warunkiem, że wszystko to dzieje się w wątku głównym aplikacji). Natomiast sposobem znacznie wygodniejszym i bezpieczniejszym byłoby emitowanie sygnału z odpowiednimi danymi i połączenie go z odpowiednim slotem w GUI.

0

Ok ale uzywajac np. connect jako 3 parametr musze podac obiekt do ktorego chce wyslac te dane a aktualnie w tej klasie wysylajacej nie przechowuje wskaznika do takiego obiektu bo jest tam niepotrzebny (ba, nawet jak probowalem przekazac do klasy wysylajacej wskaznik do UI to sypalo bledami)

1

QObject::connect możesz wywołać z dowolnego miejsca w kodzie. Najczęściej jest to jedna z dwóch łączonych stron, ale czasem możesz potrzebować zrobić to "z zewnątrz". Zamiast przekazywać wskaźnik do UI, spróbuj czegoś takiego:

auto * nieui = create();
QObject::connect(nieui, SIGNAL(stateChanged(QString)), ui, SLOT(setText(QString)));
0

Dzieki, a jakbym nie chcial korzystac z dobrodziejstw Qt?

0

Wtedy byś nie używał QLabel ;) Jeśli nie chcesz używać sygnałów i slotów to musisz w jakiś sposób przekazać wskaźnik do odpowiedniego QWidgetu od którego możesz się odnieść do tego elementu do twojej klasy i wywołać funkcję setText(). Z tym, że musisz wykonać to z głównego wątku aplikacji, więc nie możesz zrobić tego bezpośrednio z kodu wykonywanego w innym wątku.

0

To trochę nie dobrze, bo ja mam w metodzie run() wątku, pętlę i chcę aby w QLabelu wyświetlała mi się aktualna iteracja tej pętli. Czyli muszę po prostu zapisać aktualną iterację do pola w klasie wątku, zrobić metodę getIteration() zwracającą wskaźnik na int'a z numerem a potem przypisać to w głównym wątku do labela (tak żeby label "wyświetlał" wskaźnik (a raczej to na co wskazuje) i dzieki temu będzie automatycznie aktualizowany. Tak?

1

W tym przypadku wysłanie sygnału iterationChanged(int) wydaje się idealnym rozwiązaniem. Jeśli koniecznie chcesz zapisać do pola klasy, które będzie odczytywane z innego wątku, musisz zapewnić synchronizację (Qt robi to za Ciebie) - najlepiej przez zmienne typu atomic, ale możesz się też bawić w muteksy i inne takie.

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0