Потоки в операционной системе являются основной реализующей длительных операций. Для предотвращения зависания интерфейса или основного рабочего процесса стоит использовать потоки. В поток нельзя выносить работу основного интерфейса, следует выносить только основную задачу, которая требует длительного времени для выполнения. Поток выполняется как независимая задача внутри запущенного процесса и разделяет общее адресное пространство вместе с глобальными данными.
При работе с потоками не следует использовать глобальные данные, это может привести к неожиданным результатам и усложнению отладки программы.
При работе в однопоточном режиме код программы выполняется последовательно и при длительной операции происходят задержка выполнения основного кода программы. Из таких операций можно выделить такие как считывания большого файла, математические расчеты ожидание ответа на запрос и многие другие.
Использование потоков должно быть обоснованно, не стоит злоупотреблять и усложнять приложение там, где это не требуется. Отладка потоков требует больше времени и понимания происходящего. А неправильное применение может привести к снижению скорости и новым ошибкам которые могут появится только в результате детального тестирования. Но при правильном и обоснованном понимании это незаменимая вещь.
Многопоточное программирование начинается с класса, который называется QThread.
Для реализации многопоточного приложения можно выделить два метода. Первый метод это переопределение его виртуального метода run() который отвечает за выполнение операции в новом потоке. И второй метода это создание нового экземпляра функции QThread и помещение его в объект QObject используя moveToThread (QThread *) экземпляра QObject и вызова start() для экземпляра QThread. И установки правильного соединения сигнал слот.
Рассмотрим поподробней первый метод. Метод переопределения виртуального метода run().
Пример использования QThread
Создадим новый класс с наследованием от QThread
#include <QThread>
class Worker : public QThread
{
Q_OBJECT
protected:
void run ()
{
//Код который будет выполнятся в новом потоке
}
}
Далее создаем объект класса и вызываем метод start(), который вызовет переопределенный метод run()
Worker thread; thread.start();
Поток запустится и завершится сразу после выполнения кода написанного в run().
Такой метод подходит для небольших задач где не требуется контролировать выполнения или ожидание другого действия.
Давайте поподробней рассмотрим данный участок кода.
Сперва подключается библиотека #include <QThread>
Далее создается класс и наследуется от QThread
class Worker : public QThread
Далее идет макрос Q_OBJECT который при компиляции создаст метаобъектный код для класса.
Метаобъектный код необходим для связывания сигналов и слотов.
Далее идет метод переопределения функции run()
protected:
void run();
Рассмотрим второй метод реализации через создание нового экземпляра QThread.
При создание нового экземпляра переопределяются сигналы и слоты необходимые для оптимальной работы.
При создание нового экземпляра переопределяются сигналы и слоты необходимые для оптимальной работы.
Класс Worker начинает работу при старте потока и заканчивает при сигнале finished().
// Пример реализации класса
class Worker : public QObject {
Q_OBJECT
public:
Worker();
~Worker();
public slots:
void process();
signals:
void finished();
private:
};
// Реализации методов класса.
Worker::Worker() {
}
Worker::~Worker() {
}
void Worker::process() {
emit finished();
}
QThread* thread = new QThread; Worker* worker = new Worker(); worker->moveToThread(thread); connect(thread, SIGNAL(started()), worker, SLOT(process())); connect(worker, SIGNAL(finished()), thread, SLOT(quit())); connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater())); connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); thread->start();
Стоит добавить что при использования конструктора new Worker(), не стоит создавать том другие классы, потому что они будут созданы в не в создаваемом потоке, так как перемещение в новый поток происходит через moveToThread(). При необходимости можно создать функцию initialize() и вызвать ее после moveToThread().
QThread* thread = new QThread; Worker* worker = new Worker(); worker->moveToThread(thread); worker-> initialize(); connect(thread, SIGNAL(started()), worker, SLOT(process())); connect(worker, SIGNAL(finished()), thread, SLOT(quit())); connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater())); connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); thread->start();
