[Serie de Programación QT-25]: Mecanismo de Subprocesos Múltiples - Introducción a QThread y MoveToThread

Tabla de contenido

Capítulo 1 Introducción

1.1 El propósito de los subprocesos múltiples

1.2 Cómo usar el multiproceso QThread

1.3 QT admite pasos de varias líneas

Capítulo 2 Subprocesos Q

2.1 Resumen

2.2 mover a hilo


Capítulo 1 Introducción

1.1 El propósito de los subprocesos múltiples

La clase Q Thread proporciona una forma independiente de la plataforma para administrar los hilos.

El propósito principal de crear subprocesos en Qt es usar subprocesos para manejar esas operaciones en segundo plano que consumen mucho tiempo , como cálculos masivos, copia de archivos grandes, transmisión de red, etc.

QT (también conocido como Qt Framework) es una biblioteca de C++ para desarrollar aplicaciones multiplataforma. Ofrece un amplio conjunto de funciones, incluida la compatibilidad con subprocesos múltiples.

Multithreading es una técnica para manejar varias tareas al mismo tiempo , lo que puede mejorar la capacidad de respuesta y el rendimiento de una aplicación .

1.2 Cómo usar el multiproceso QThread

1. Hilo personalizado hereda la clase QThread

2.  QObject ::moveToThread()

Capítulo

2.1 Pasos para que QThread admita subprocesos múltiples

El uso de subprocesos múltiples en QT se puede lograr a través de los siguientes pasos:

  1. Introducir clases relacionadas con subprocesos múltiples de Qt: Introducir/incluir archivos de encabezado de las siguientes clases en su código: QThread , QRunnable , QThreadPool, QObject, etc.

  2. Cree una clase que herede de QThread o QRunnable: esta clase se usará para realizar tareas que deben ejecutarse en un subproceso en segundo plano . Si necesita más flexibilidad y control, puede optar por heredar de QThread ; de lo contrario , es más sencillo heredar de QRunnable .

  3. Anule el método run(): anule el método run() en su clase personalizada e implemente la lógica de su tarea en él.

  4. Cree e inicie un hilo: cree una instancia de su clase personalizada en el hilo principal y luego llame al método start() para iniciar el hilo. Esto llamará automáticamente a su método run().

El siguiente es un código de muestra simple que muestra cómo usar subprocesos múltiples en QT:

#include <QThread>
#include <QDebug>

class MyThread : public QThread
{
    Q_OBJECT
public:
    void run() override
    {
        // 执行你的任务逻辑
        qDebug() << "Hello from thread";
    }
};

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    MyThread thread;
    thread.start();

    // 主线程继续执行其他任务
    qDebug() << "Hello from main thread";

    return app.exec();
}

En el ejemplo anterior, creamos una clase llamada MyThread, que hereda de QThread. Anulamos el método run(), que imprime un mensaje. En la función main(), creamos una instancia de MyThread y llamamos a su método start() para iniciar el hilo. Mientras tanto, el hilo principal continúa realizando otras tareas . Al ejecutar este programa, verá dos mensajes, uno del hilo principal y otro del hilo secundario.

Debe tenerse en cuenta que cuando se utiliza la programación de subprocesos múltiples, el acceso a datos compartidos entre subprocesos debe manejarse con cuidado para evitar condiciones de carrera y otros problemas relacionados con subprocesos. Qt proporciona algunas herramientas de sincronización, como mutexes y semáforos , para ayudarlo a lograr un acceso seguro a los datos entre subprocesos.

Qt proporciona un potente soporte de subprocesos múltiples, lo que permite a los desarrolladores implementar fácilmente la simultaneidad y el procesamiento paralelo en las aplicaciones.

Los siguientes son algunos componentes y características importantes de la compatibilidad con subprocesos múltiples de Qt:

  1. Clase QThread: QThread es la clase básica de programación multiproceso proporcionada por Qt. Los desarrolladores pueden crear sus propias clases de subprocesos al heredar la clase QThread e implementar la función run() para definir la lógica de ejecución del subproceso.

  2. Mecanismo de señal y ranura: el mecanismo de señal y ranura de Qt es un poderoso mecanismo de comunicación entre subprocesos . La comunicación de objetos entre subprocesos se puede implementar a través de señales y ranuras, sin manipulación directa de datos compartidos . Este mecanismo puede reducir efectivamente la complejidad de la programación de subprocesos múltiples.

  3. Clase QMutex: QMutex es una clase mutex utilizada para proteger el acceso a datos compartidos. Con QMutex, puede asegurarse de que solo un subproceso pueda acceder al área de código protegido a la vez, evitando así las condiciones de carrera.

  4. Clase QWaitCondition: QWaitCondition es una clase de variable de condición para operaciones de espera y activación entre subprocesos. A través de QWaitCondition, puede implementar el subproceso para esperar hasta que se cumpla una determinada condición y reactivar el subproceso cuando se cumpla la condición.

  5. Clase QThreadPool: QThreadPool es una clase de grupo de subprocesos responsable de administrar y programar subprocesos. A través de QThreadPool, puede enviar tareas al grupo, y el grupo de subprocesos administrará automáticamente el ciclo de vida de los subprocesos y la ejecución de las tareas por usted.

Además de los componentes principales mencionados anteriormente, Qt también proporciona otras clases y herramientas auxiliares, como QReadWriteLock (bloqueo de lectura y escritura), QSemaphore (semáforo), QtConcurrent (marco de programación paralelo), etc. Estas clases y herramientas pueden ayudarlo a manejar mejor la programación de subprocesos múltiples.

Cuando utilice Qt multithreading, debe prestar atención a los siguientes puntos:

  1. Evite el acceso directo a los datos compartidos: intente no manipular directamente los datos compartidos , pero garantice la seguridad de los subprocesos a través de mecanismos como señales y ranuras o utilizando mutexes.

  2. Evite bloquear el subproceso principal durante mucho tiempo: si hay una operación que consume mucho tiempo en el subproceso principal, debe colocarse en un subproceso de fondo para evitar bloquear el subproceso principal y garantizar la capacidad de respuesta de la aplicación.

  3. Tenga cuidado con los datos que se pasan entre subprocesos: al pasar datos entre subprocesos, debe prestar atención a la validez y el ciclo de vida de los datos para evitar problemas como punteros colgantes o referencias no válidas.

El soporte de subprocesos múltiples de Qt puede ayudarlo a escribir aplicaciones de subprocesos múltiples más fácilmente y brinda una gran comodidad en el procesamiento paralelo y concurrente. Al usar juiciosamente las capacidades de subprocesos múltiples de Qt, puede mejorar el rendimiento y la capacidad de respuesta de sus aplicaciones y hacer un mejor uso de la potencia de cómputo de los procesadores de múltiples núcleos.

2.2 Explicación QThread

QThread es una clase proporcionada por Qt para crear y administrar hilos en las aplicaciones.

Encapsula las operaciones de subprocesos subyacentes, lo que hace que la programación multiproceso sea más conveniente y manejable.

Estos son algunos pasos generales y usos comunes del uso de QThread:

  1. Cree una subclase QThread personalizada y anule su función run(). La función run() es el punto de entrada que se ejecuta cuando se inicia el subproceso, donde puede escribir la lógica de su subproceso.
class MyThread : public QThread {
public:
    void run() override {
        // 编写线程逻辑
    }
};
  1. Cree una instancia de la clase MyThread y llame a la función start() para iniciar el hilo.
MyThread* thread = new MyThread();
thread->start();

  1. Opcionalmente, conecte señales y ranuras para la comunicación entre subprocesos . Puede usar el mecanismo de señales y ranuras de Qt para comunicarse entre el hilo principal y los hilos secundarios.
connect(someObject, &SomeObject::someSignal, thread, &MyThread::someSlot);
// 或者反向连接
connect(thread, &MyThread::someSignal, someObject, &SomeObject::someSlot);

  1. Cuando sea necesario, se puede llamar a la función wait() para esperar el final del subproceso secundario. También puede llamar a la función quit() o terminar() para detener el hilo.
thread->wait();      // 等待线程结束
thread->quit();      // 请求线程退出
thread->terminate(); // 强制终止线程

  1. Verifique el estado de los hilos y controle el comportamiento de los hilos. Puede usar funciones QThread para obtener el estado actual del hilo, como isRunning(), isFinished(), isInterruptionRequested(), etc.
if (thread->isRunning()) {
    // 线程正在运行
}

QThread también proporciona otras funciones auxiliares, como establecer la prioridad de los subprocesos, establecer el tamaño de la pila de subprocesos, el ciclo de eventos y el procesamiento de eventos, etc.

Cabe señalar que heredar directamente QThread y reescribir la función run() es una forma tradicional de usar QThread. Pero a partir de la versión Qt 5.2, Qt proporciona una forma más sencilla y recomendada de manejar subprocesos múltiples, que es usar el mecanismo de señal y ranura de Qt en combinación con QRunnable y QThreadPool o Qt Concurrent.

Espero que esta información sea útil para crear y administrar subprocesos utilizando la clase QThread. Si tiene más preguntas, no dude en preguntar.

2.3 Funciones miembro de QThread

La clase QThread es una clase que se utiliza para crear y administrar subprocesos en Qt. Proporciona muchas funciones miembro para administrar el comportamiento y el estado de los subprocesos.

Las siguientes son algunas funciones miembro comunes en la clase QThread:

  1. start(): Inicie el subproceso para que comience a ejecutar el código en la función run() como un subproceso independiente .

  2. run(): la función virtual que debe volver a implementarse en la subclase define la lógica del código principal que ejecuta el subproceso.

  3. wait(): Bloquea el subproceso actual hasta que se complete la ejecución del subproceso.

  4. sleep(): Hace que el subproceso actual entre en suspensión durante el número especificado de milisegundos.

  5. terminate(): Termina la ejecución del hilo. Este es un método crudo y generalmente no recomendado.

  6. quit(): detiene el ciclo de eventos del subproceso e inicia la ejecución del subproceso. Usualmente se usa con bucle de eventos (event loop).

  7. msleep(): Hace que el subproceso actual entre en suspensión durante el número especificado de milisegundos.

  8. usleep(): Hace que el subproceso actual entre en suspensión durante el número especificado de microsegundos.

  9. isRunning(): determina si el subproceso se está ejecutando.

  10. currentThreadId(): Devuelve el identificador único del hilo actual.

  11. priority() /  setPriority(): Obtener o establecer la prioridad del hilo.

  12. finished(): La señal emitida cuando se completa la ejecución del subproceso.

  13. started(): La señal emitida cuando el subproceso comienza a ejecutarse.

  14. finished(): La señal enviada por el objeto QThread después de que se completa la ejecución del hilo.

  15. moveToThread(QThread* thread): Mueva el objeto al subproceso especificado para ejecutarlo.

Estas son solo algunas de las funciones de miembros comunes en la clase QThread, al usar estas funciones, puede administrar la ejecución, el estado y el comportamiento del subproceso. De acuerdo con los requisitos específicos de la aplicación, se pueden usar más funciones de miembros de QThread para completar la administración y las operaciones de subprocesos.

Capítulo 3  moveToThread

3.1 概述

moveToThread()Es una función miembro en Qt, que se usa para mover el objeto al subproceso especificado para su ejecución.

Le permite administrar contextos de ejecución de subprocesos para objetos en aplicaciones multiproceso .

A continuación se muestra moveToThread()la sintaxis y el uso de la función:

void QObject::moveToThread(QThread* thread)
  • thread: Puntero al subproceso de destino.

moveToThread()El rol de la función es asociar el objeto que llama a la función con el hilo especificado.

Esto significa que los métodos y eventos del objeto se ejecutarán en el contexto del subproceso de destino , no en el subproceso de llamada (el subproceso que creó el objeto) .

Aquí hay un moveToThread()ejemplo usando la función:

#include <QObject>
#include <QDebug>
#include <QThread>

class Worker : public QObject {
    Q_OBJECT

public slots:
    void doWork() {
        qDebug() << "Worker thread:" << QThread::currentThread();
        // 执行耗时操作
    }
};

int main(int argc, char *argv[]) {
    QCoreApplication app(argc, argv);

    qDebug() << "Main thread:" << QThread::currentThread();


    // 创建一个新的管理线程上下文的对象
    QThread* workerThread = new QThread;

    // 创建一个可以task对象,该对象可以执行某种操作,比如while循环的函数
    Worker* worker = new Worker;

    // 将Worker对象移到 workerThread 线程
    // 如果没有这行代码,则worker的task在创建worker线程中执行,而不是目标线程上下文中执行
    worker->moveToThread(workerThread);

    // 当线程启动时,执行 Worker 的 doWork() 槽函数

    // 线程QThread也是类的对象,因此,可以通过槽函数和信号在对象间通信:
    // 在QThread对象中执行另一个需要循环执行的对象的成员函数(while循环)
    QObject::connect(workerThread, &QThread::started, worker, &Worker::doWork);

    //在QThread对象的上下文中发送一个started信号,在QThread线程对象的上下文中执行worker对象中的槽函数,该槽函数是一个while循环执行的函数。
    workerThread->start();

    return app.exec();
}

En el ejemplo anterior, creamos una Workerclase que hereda de QObject. WorkerSe define un método en la clase doWork()para realizar un trabajo que requiere mucho tiempo.

En la función principal, creamos un workerobjeto y un workerThreadobjeto hilo. Luego, use moveToThread()para workermover el objeto dentro workerThreaddel hilo. De esta forma, los métodos workerdel objeto se ejecutarán en el hilo.doWork()workerThread

Al conectar workerThreadla startedseñal con workerla función de ranura, el método se llamará automáticamente doWork()cuando se inicie el subproceso .doWork()

Cabe señalar que moveToThread()la función solo se puede llamar en el hilo al que pertenece el objeto, de lo contrario, causará un comportamiento indefinido. Por lo tanto, se recomienda llamar a la función lo antes posible después de crear el objeto moveToThread()para garantizar que el objeto se ejecute en el subproceso previsto.

moveToThread()Las funciones son muy útiles en la programación de subprocesos múltiples, lo que le permite mover objetos al subproceso apropiado y ejecutar métodos asociados y manejo de eventos en ese subproceso. Esto ayuda a separar las operaciones que consumen mucho tiempo de las interacciones de la interfaz de usuario, manteniendo la capacidad de respuesta de la aplicación.

Capítulo 4 QHilo ymoveToThread创建线程的比较

4.1 代码实例

#include "mainwindow.h"

#include <QApplication>
#include <QThread>
#include <QDebug>

#include <QObject>
#include <QWidget>



class MyThread : public QThread {
public:
    void run() override {
        qDebug() << "Run Thread started";

        // 在这里执行耗时任务
        int i = 0;
        while(1)
        {
            sleep(3);
            qDebug() << i << ": run thread:" << QThread::currentThread();
            i++;
        }

        qDebug() << "Run Thread finished";
    }
};



class MyWorker : public QObject {
    Q_OBJECT

public slots:
    void doWork() {

        qDebug() << "doWork Thread started";

        while(1){
            qDebug() << "Worker thread:" << QThread::currentThread();
            // 执行耗时操作
            QThread::sleep(3);
        }

        qDebug() << "doWork Thread finished";
    }
};



int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;

    qDebug() << "Main thread started: " << QThread::currentThread() ;

    // 创建并启动run线程1
    MyThread thread_run1;
    thread_run1.start();

    // 创建并启动run线程2
    MyThread thread_run2;
    thread_run2.start();



    //创建一个管理线程上下文的对象
    QThread* workerThread1 = new QThread;

    //创建一个task对象
    MyWorker* myworker1 = new MyWorker;

    // 将 Worker 对象移到 workerThread 线程
    //myworker1->moveToThread(workerThread1);

    // 当线程启动时,执行 Worker 的 doWork() 槽函数
    // 线程也是一个对象,因此可以使用对象之间的通信机制,进行通信
    QObject::connect(workerThread1, &QThread::started, myworker1, &MyWorker::doWork);

    workerThread1->start();


    //创建一个管理线程上下文的对象
    QThread* workerThread2 = new QThread;

    //创建一个task对象
    MyWorker* myworker2 = new MyWorker;

    // 将 Worker 对象移到 workerThread 线程
    myworker2->moveToThread(workerThread2);

    // 当线程启动时,执行 Worker 的 doWork() 槽函数
    // 线程也是一个对象,因此可以使用对象之间的通信机制,进行通信
    QObject::connect(workerThread2, &QThread::started, myworker2, &MyWorker::doWork);

    workerThread2->start();

    qDebug() << "Main thread finished" << QThread::currentThread() ;

    w.show();
    return a.exec();
}

4.2 输出结果

Subproceso principal iniciado: QThread ( 0x9c7770 )

Hilo principal terminado QThread( 0x9c7770 )

Ejecutar Subproceso iniciado

Ejecutar Subproceso iniciado

Hilo iniciado

Subproceso de trabajo: QThread ( 0x30593d0 )

Hilo iniciado

Subproceso de trabajo: QThread( 0x9c7770//No hay myworker1->moveToThread(workerThread1), por lo que se ejecuta en el contexto del subproceso principal, por lo que la ejecución es relativamente lenta, por detrás de myworker2 en el contexto de workerThread2

0: ejecutar subproceso: QThread ( 0x8bfc30 )

0: ejecutar subproceso: QThread ( 0x8bfc20 )

Subproceso de trabajo: QThread(0x30593d0)

Subproceso de trabajo: QThread (0x9c7770)

1: ejecutar hilo: QThread (0x8bfc30)

1: ejecutar hilo: QThread (0x8bfc20)

Subproceso de trabajo: QThread(0x30593d0)

Subproceso de trabajo: QThread (0x9c7770)

Supongo que te gusta

Origin blog.csdn.net/HiWangWenBing/article/details/131715195
Recomendado
Clasificación