Qt Creator - wątki

0

Witam, potrzebuję użyć wątków w Qt Creator. Używam biblioteki QThread. Oto mój kod:

class pojazd : public QThread
{
    Q_OBJECT;
public:
    void funkcja();
};

pojazd::funkcja()
{
    tu cos robi;
}

int main()
{
    pojazd nowy;
    nowy.start();
    nowy.funkcja();

}

Nowe wątki się tworzą, jednak funkcja jest wywoływana poza wątkiem, nawet gdy jest nazwana jako run(); Znalazłem gdzieś też kod, gdzie klasa była dziedziczona zwykle po QObject, a następnie jej sloty były łączone ze slotem started() nowego wątku. Jednak nie wiem jak użyć tej drugiej metody. Macie jakieś pomysły, dajcie znać proszę...

0

Dzięki, sprawdzałem już tę witrynę, jednak nie rozumiem tego fragmentu:

QTcpSocket socket;
     // connect QTcpSocket's signals somewhere meaningful
     ...
     socket.connectToHost(hostName, portNumber);

Mam rozumieć że pisząc w main lub gdziekolwiek indziej:

moja_klasa nowy;
nowy.start();

Uruchomię funkcję run w nowym wątku?

5

Moja rada NIGDY nie dziedzicz po QThread! Tu ludzie popełniają strasznie dużo błędów. To powinno wyglądać tak:

class TwojaKlasa : public QObject {
    Q_OBJECT
public:
     TwojaKlasa(QObject *parent = 0) QObject(parent) { ... }
     ~TwojaKlasa() { ... }

public slots:
     void twojSlot1() {
          ...
          emit czesciowyWynik("tada");
          ...
          emit zrobione();
     }
     void twojSlot2() {
          ...
     }
public signals:
     void czesciowyWynik(const QString& result);
     void zrobione();
}

// Gdzieś w kodzie by uruchomić coś w innym wątku:
QThread *thread = new QThread(jakisWlasciciel);
TwojaKlasa *worker = new TwojaKlasa(); // brak parenta jest tutaj obowiązkowy
worker ->moveToThread(thread);
thread->start(); // to tylko uruchamia wątek, który będzie czekał na pracę

connect(button1, SIGNAL(clicked()),
        worker, SLOT(twojSlot1()),
        Qt::QueuedConnection); // to połączanie będzie zlecało pracę wątkowi

connect(button2, SIGNAL(clicked()),
        worker, SLOT(twojSlot2())
        Qt::QueuedConnection); // to połączanie będzie zlecało pracę wątkowi

// opcjonalne czyszczenie wątku (poprawione w związku z tym tematem: http://4programmers.net/Forum/C_i_C++/224821-poprawne_zakonczenie_pracy_watku_qthread?p=988200#id988200):
//connect(worker, SIGNAL(destroyed(QObject*)),
//        thread, SLOT(quit()));
//connect(thread, SIGNAL(finished()),
 //       thread, SLOT(deleteLater()));

// do wykonania w jakim slocie
thread->wait();
thread->deleteLater()

Jak to działa? QThread ma event loopa, który czeka na coś do pracy (wątek pozostaje zawieszony - nic nie robi). Gdy leci sygnał do slotu worker-a, sygnał jest ładnie opakowywany i przesyłany do właściwego wątku (to właśnie robi moveToThread), wątek jest wybudzany i rozpoczyna przetwarzanie właściwego slotu. Jeśli więcej sygnałów zostanie wysłanych do obiektów w tym samym wątku, to są one kolejkowanie (idealne dla komunikacji sieciowej).
Cały pattern upraszcza proces komunikacji między wątkami.

Poza tym masz QThreadPool, który jest prosty w użyciu (nadaje się do zlecenia pojedynczej pracy), oraz freamework w przestrzeni nazw QtConcurrent do przetwarzanie kolekcji danych w wielu wątkach.

0

Mam parę pytań, bo coś źle robię.

  • czy mogę dać sobie wskaźnik QThread *thread jako składnik klasy i przydzielić mu pamięć w konstruktorze klasy?
  • w ciele slotu, który chcę wywołać w nowym wątku, muszę umieścić exec, lub emit?
  • czy można przyłączyć slot do jakieś zwykłej metody tej klasy, zamiast do zdarzenia clicked przycisku (chcę tworzyć wątki i wywoływać w nich funkcje w jakieś innej funkcji)

Dodam jeszcze, że zrobiłem prosty program, gdzie w slocie, który chcę odpalić w nowym wątku przesuwam w pętli label o 1 w dół. Po kliknięciu przycisku, po którym się powinna wykonać akcja tego slotu wyskakuje błąd:
Microsoft Visual C++ Runtime Library
This application has requested the Runtime to terminate it in an unusual way.

Kompilator wyrzuca:
ASSERT failure in QCoreApplication: "Cannot send events to objects owned by a different thread. Current thread 55a278. Receiver 'label' (of type 'QLabel') was created in thread 3974d0", file kernel/qcoreapplication.cpp, line 532
Invalid parameter passed to C runtime function.
Invalid parameter passed to C runtime function.
QObject: timers cannot be stopped from another thread

1
Chev_Lucas napisał(a):
  • czy mogę dać sobie wskaźnik QThread *thread jako składnik klasy i przydzielić mu pamięć w konstruktorze klasy?
    Możesz ale ja by tak nie robił.
Chev_Lucas napisał(a):
  • w ciele slotu, który chcę wywołać w nowym wątku, muszę umieścić exec, lub emit?
    ciało sloty ma wyglądać tak jak ty chesz. Na pewno nie ma być tam exec, a emit dałem jako przykład jak należy przekazać wynik działania slotu.
Chev_Lucas napisał(a):
  • czy można przyłączyć slot do jakieś zwykłej metody tej klasy, zamiast do zdarzenia clicked przycisku (chcę tworzyć wątki i wywoływać w nich funkcje w jakieś innej funkcji)
    źródłem sygnału może być cokolwiek, znowu dałem buttona i slota clicked jako przykład.
Chev_Lucas napisał(a):

Dodam jeszcze, że zrobiłem prosty program, gdzie w slocie, który chcę odpalić w nowym wątku przesuwam w pętli label o 1 w dół. Po kliknięciu przycisku, po którym się powinna wykonać akcja tego slotu wyskakuje błąd:
Microsoft Visual C++ Runtime Library
This application has requested the Runtime to terminate it in an unusual way.
Nie wolno modyfikować elementów UI z innych wątków. Jeśli zrobisz to przez sygnały i sloty to Qt ładnie prześle sygnał do właściwego wątku w którym slot ma być wykonany.

0

Utworzyłem nową klasę, której nie przydzielam do żadnego wątku. Klasa ta posiada dwa sloty, w których steruję labelami z formy. W klasie, którą ładuję do wątku mam dwa sygnały z identycznymi listami argumentów jakie mają sloty z pierwszej klasy.
Jednak, gdy wywołuję funkcję pisząc

nowy.fun1(label, x, y);

to otrzymam żądany efekt, jak np. przesunięcie się etykiety.
Ale pisząc w klasie umieszczonej w wątku

emit signal1(label, x, y);
//oraz gdzieś po utworzeniu klasy:
connect(this, SIGNAL(signal1(QLabel*, int, int)), &nowy, SLOT(fun1(QLabel*, int, int)), Qt::QueuedConnection);

Nie widać żadnych efektów. Nie wiem dlaczego :/

0

Już sobie poradziłem, emitowany sygnał jest odbierany prawidłowo i wykonuje się akcja z przypisanego slotu.
Natomiast, mam inny problem, potrzebuję w trakcie działania powiedzmy 8 wątków czytać jedną zmienną składową klasy korzystając z tablicy wskaźników do obiektów tej klasy.
Gdy wątki jeszcze nie są odpalone to działa ładnie, ale gdy wątki działają program się sypie. Pewnie dlatego, że jednocześnie zmienna jest zapisywana przez jeden obiekt w jednym wątku i odczytywana przez inny.

0

Zainteresuj się mutexami (http://doc.qt.digia.com/qt/qmutex.html), właśnie po to one są :)

0

A wiecie może jak podczas działania wątku wywołać funkcję tak, żeby czekać na jej zakończenie, a ta funkcja się wykonywała w wątku głównym (chodzi mi o dokonywanie losowania - jak mam je w wątku pobocznym, to coś nie działa ;/)
Czy da się zrobić coś takiego, że funkcja tylko w pewnym miejscu jest w wątku, potem wykona coś poza wątkiem i tak w pętli...

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