Qt
Потоки и параллелизм
Поиск…
замечания
Несколько заметок, которые уже упоминаются в официальных документах здесь и здесь :
- Если у объекта есть родительский элемент, он должен находиться в том же потоке, что и родительский, то есть его нельзя перенести в новый поток, и вы не можете установить родительский объект в объект, если родительский объект и объект живут в разных потоках
- Когда объект перемещается в новый поток, все его дочерние элементы также перемещаются в новый поток
- Вы можете только нажимать объекты на новый поток. Вы не можете вытащить их в новый поток, т.
moveToThread
Вы можете только вызватьmoveToThread
из потока, в котором объект в настоящее время живет
Основное использование QThread
QThread
- это дескриптор потока платформы. Он позволяет управлять потоком, отслеживая его срок службы и запрашивая, чтобы он завершил свою работу.
В большинстве случаев наследование от класса не рекомендуется. Метод run
умолчанию запускает цикл событий, который может отправлять события объектам, живущим в классе. Кросс-нитки сигнальных слотов реализуются путем отправки QMetaCallEvent
на целевой объект.
Экземпляр QObject
можно перенести в поток, где он будет обрабатывать свои события, такие как события таймера или вызовы слотов / методов.
Чтобы выполнить работу над потоком, сначала создайте свой собственный рабочий класс, полученный из QObject
. Затем переместите его в поток. Объект может автоматически запускать свой собственный код, например, используя 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);
}
};
Если ваш работник должен быть эфемерным и существует только во время его работы, лучше всего представить функтор или потокобезопасный метод для выполнения в пуле потоков через QtConcurrent::run
.
Прогон QtConcurrent
Если вы обнаружите, что управление QThreads и низкоуровневыми примитивами, такими как мьютексы или семафоры, слишком сложны, пространство имен Qt Concurrent - это то, что вы ищете. Он включает классы, которые позволяют управлять потоками более высокого уровня.
Давайте посмотрим на Concurrent Run. QtConcurrent::run()
позволяет запускать функцию в новом потоке. Когда вы хотели бы использовать его? Когда у вас длительная операция, и вы не хотите создавать поток вручную.
Теперь код:
#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();
}
Итак, все просто: когда нам нужно запустить другую функцию в другом потоке, просто вызовите QtConcurrent::run
, pass function и его параметры, и все!
QFuture
представляет результат нашего асинхронного вычисления. В случае QtConcurrent::run
мы не можем отменить выполнение функции.
Вызов слотов из других потоков
Когда цикл выполнения Qt используется для выполнения операций, и пользователь, не относящийся к Qt-saavy, должен взаимодействовать с этим циклом событий, запись слота для обработки регулярных вызовов из другого потока может упростить работу для других пользователей.
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