Потоки в операционной системе являются основной реализующей длительных операций. Для предотвращения зависания интерфейса или основного рабочего процесса стоит использовать потоки. В поток нельзя выносить работу основного интерфейса, следует выносить только основную задачу, которая требует длительного времени для выполнения. Поток выполняется как независимая задача внутри запущенного процесса и разделяет общее адресное пространство вместе с глобальными данными.
При работе с потоками не следует использовать глобальные данные, это может привести к неожиданным результатам и усложнению отладки программы.
При работе в однопоточном режиме код программы выполняется последовательно и при длительной операции происходят задержка выполнения основного кода программы. Из таких операций можно выделить такие как считывания большого файла, математические расчеты ожидание ответа на запрос и многие другие.
Использование потоков должно быть обоснованно, не стоит злоупотреблять и усложнять приложение там, где это не требуется. Отладка потоков требует больше времени и понимания происходящего. А неправильное применение может привести к снижению скорости и новым ошибкам которые могут появится только в результате детального тестирования. Но при правильном и обоснованном понимании это незаменимая вещь.
Многопоточное программирование начинается с класса, который называется 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();