Qt
Wątek i współbieżność
Szukaj…
Uwagi
Kilka uwag, które są już wspomniane w oficjalnych dokumentach tutaj i tutaj :
- Jeśli obiekt ma element nadrzędny, musi znajdować się w tym samym wątku co element nadrzędny, tzn. Nie można go przenieść do nowego wątku, ani nie można ustawić elementu nadrzędnego na obiekt, jeśli element nadrzędny i obiekt żyją w różnych wątkach
- Kiedy obiekt jest przenoszony do nowego wątku, wszystkie jego elementy potomne są również przenoszone do nowego wątku
- Możesz tylko wypychać obiekty do nowego wątku. Nie możesz wyciągnąć ich do nowego wątku, tzn. Możesz wywołać
moveToThread
tylko z wątku, w którym aktualnie mieszka obiekt
Podstawowe użycie QThread
QThread
to uchwyt wątku platformy. Pozwala zarządzać wątkiem, monitorując jego żywotność i prosząc o zakończenie pracy.
W większości przypadków dziedziczenie po klasie nie jest zalecane. Domyślną run
metoda rozpoczyna pętlę zdarzeń, który może wywoływać zdarzeń do obiektów żyjących w klasie. Połączenia między wątkami a szczeliną sygnałową są realizowane przez wysłanie QMetaCallEvent
do obiektu docelowego.
Instancję QObject
można przenieść do wątku, w którym będzie przetwarzać swoje zdarzenia, takie jak zdarzenia timera lub wywołania slotu / metody.
Aby wykonać pracę nad wątkiem, najpierw utwórz własną klasę roboczą, która wywodzi się z QObject
. Następnie przenieś go do wątku. Obiekt może uruchomić swój własny kod automatycznie, np. Za pomocą QMetaObject::invokeMethod()
.
#include <QObject>
class MyWorker : public QObject
{
Q_OBJECT
public:
Q_SLOT void doWork() {
qDebug() << "doWork()" << QThread::currentThread();
// and do some long operation here
}
MyWorker(QObject * parent = nullptr) : QObject{parent} {}
};
class MyController : public QObject
{
Q_OBJECT
Worker worker;
QThread workerThread;
public:
MyController() {
worker.moveToThread(&workerThread);
// provide meaningful debug output
workerThread.setObjectName("workerThread");
workerThread.start();
// the thread starts the event loop and blocks waiting for events
}
~MyController() {
workerThread.quit();
workerThread.wait();
}
void operate() {
// Qt::QueuedConnection ensures that the slot is invoked in its own thread
QMetaObject::invokeMethod(&worker, "doWork", Qt::QueuedConnection);
}
};
Jeśli Twój pracownik powinien być efemeryczny i istnieć tylko w trakcie wykonywania pracy, najlepiej podać funktor lub metodę bezpieczną dla wątków w celu wykonania w puli wątków za pomocą QtConcurrent::run
.
QtConcurrent Run
Jeśli zarządzanie QThreads i prymitywy niskiego poziomu, takie jak muteksy lub semafory, są zbyt skomplikowane, przestrzeń nazw Qt Concurrent jest tym, czego szukasz. Zawiera klasy, które pozwalają na więcej zarządzania wątkami na wysokim poziomie.
Spójrzmy na współbieżny bieg. QtConcurrent::run()
pozwala na uruchamianie funkcji w nowym wątku. Kiedy chcesz go użyć? Gdy masz trochę operacji i nie chcesz ręcznie tworzyć wątku.
Teraz kod:
#include <qtconcurrentrun.h>
void longOperationFunction(string parameter)
{
// we are already in another thread
// long stuff here
}
void mainThreadFunction()
{
QFuture<void> f = run(longOperationFunction, "argToPass");
f.waitForFinished();
}
Więc rzeczy są proste: kiedy musimy uruchomić inną funkcję w innym wątku, wystarczy wywołać QtConcurrent::run
, pass function i jej parametry i to wszystko!
QFuture
przedstawia wynik naszego obliczenia asynchronicznego. W przypadku QtConcurrent::run
nie możemy anulować wykonania funkcji.
Wywoływanie slotów z innych wątków
Gdy do wykonywania operacji używana jest pętla zdarzeń Qt, a użytkownik spoza Qt-saavy musi wchodzić w interakcje z tą pętlą zdarzeń, napisanie szczeliny do obsługi regularnych wywołań z innego wątku może uprościć innym użytkownikom.
main.cpp:
#include "OperationExecutioner.h"
#include <QCoreApplication>
#include <QThread>
int main(int argc, char** argv)
{
QCoreApplication app(argc, argv);
QThread thrd;
thrd.setObjectName("thrd");
thrd.start();
while(!thrd.isRunning())
QThread::msleep(10);
OperationExecutioner* oe = new OperationExecutioner;
oe->moveToThread(&thrd);
oe->doIt1(123,'A');
oe->deleteLater();
thrd.quit();
while(!thrd.isFinished())
QThread::msleep(10);
return 0;
}
OperationExecutioner.h:
#ifndef OPERATION_EXECUTIONER_H
#define OPERATION_EXECUTIONER_H
#include <QObject>
class OperationExecutioner : public QObject
{
Q_OBJECT
public slots:
void doIt1(int argi, char argc);
};
#endif // OPERATION_EXECUTIONER_H
OperationExecutioner.cpp:
#include "OperationExecutioner.h"
#include <QMetaObject>
#include <QThread>
#include <QDebug>
void OperationExecutioner::doIt1(int argi, char argc)
{
if (QThread::currentThread() != thread()) {
qInfo() << "Called from thread" << QThread::currentThread();
QMetaObject::invokeMethod(this, "doIt1", Qt::QueuedConnection,
Q_ARG(int,argi), Q_ARG(char,argc));
return;
}
qInfo() << "Called from thread" << QThread::currentThread()
<< "with args" << argi << argc;
}
OperationExecutioner.pro:
HEADERS += OperationExecutioner.h
SOURCES += main.cpp OperationExecutioner.cpp
QT -= gui