Qt
Threading en gelijktijdigheid
Zoeken…
Opmerkingen
Een paar opmerkingen die hier en hier al zijn vermeld in de officiële documenten:
- Als een object een bovenliggend item heeft, moet het zich in dezelfde thread bevinden als het bovenliggende item, dat wil zeggen dat het niet naar een nieuwe thread kan worden verplaatst. U kunt ook geen bovenliggend object instellen als het bovenliggende object en het object in verschillende threads wonen
- Wanneer een object naar een nieuwe thread wordt verplaatst, worden alle onderliggende items ook naar de nieuwe thread verplaatst
- U kunt alleen objecten naar een nieuwe thread pushen . Je kunt ze niet trekken om een nieuwe thread, dwz u kunt alleen bellen
moveToThread
van de draad waar het object momenteel in leeft
Basisgebruik van QThread
QThread
is een handvat voor een platformthread. Hiermee kunt u de thread beheren door de levensduur ervan te controleren en te vragen dat hij zijn werk afrondt.
In de meeste gevallen wordt inherent aan de klasse niet aanbevolen. De standaard run
start een gebeurtenislus die gebeurtenissen kan verzenden naar objecten die in de klasse wonen. Cross-thread signaal- QMetaCallEvent
worden geïmplementeerd door een QMetaCallEvent
naar het doelobject te verzenden.
Een QObject
instantie kan worden verplaatst naar een thread, waar de gebeurtenissen worden QObject
, zoals timergebeurtenissen of slot / methode-aanroepen.
Als u aan een thread wilt werken, maakt u eerst uw eigen QObject
die is afgeleid van QObject
. Verplaats het dan naar de draad. Het object kan automatisch zijn eigen code uitvoeren, bijvoorbeeld door 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);
}
};
Als uw werknemer kortstondig moet zijn en alleen bestaat terwijl het werk wordt gedaan, kunt u het beste een functor of een threadveilige methode voor uitvoering in de QtConcurrent::run
via QtConcurrent::run
.
QtConcurrent Run
Als u het beheren van QThreads en primitieven op laag niveau zoals mutexen of semaforen te complex vindt, is Qt Concurrent namespace wat u zoekt. Het bevat klassen die meer threadbeheer op hoog niveau mogelijk maken.
Laten we eens kijken naar Concurrent Run. QtConcurrent::run()
kan de functie in een nieuwe thread worden uitgevoerd. Wanneer wil je het gebruiken? Als je een lange operatie hebt en je wilt niet handmatig een thread maken.
Nu de code:
#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();
}
Dus het is eenvoudig: als we een andere functie in een andere thread moeten uitvoeren, roept u QtConcurrent::run
, pass-functie en de bijbehorende parameters aan en dat is alles!
QFuture
presenteert het resultaat van onze asynchrone berekening. In het geval van QtConcurrent::run
we de uitvoering van de functie niet annuleren.
Oproepen van slots uit andere threads
Wanneer een Qt-gebeurtenislus wordt gebruikt om bewerkingen uit te voeren en een niet-Qt-saavy gebruiker moet interageren met die gebeurtenislus, kan het schrijven van de sleuf om reguliere invocaties van een andere thread af te handelen, dingen voor andere gebruikers vereenvoudigen.
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