[Serie de programación QT-26]: mecanismo de subprocesos múltiples: comunicación entre subprocesos QT y cola de eventos de subprocesos cola de eventos, QueuedConnection

Tabla de contenido

Capítulo 1 Mecanismo de comunicación entre subprocesos

1.1 Mecanismos comunes para la comunicación entre hilos

1.2 La solución al desequilibrio entre el hilo de datos de envío y el hilo de datos de recepción

Capítulo 2 Comunicación entre objetos: comunicación de señales y ranuras

Capítulo 3 Comunicación entre subprocesos: transferencia de datos entre subprocesos


Capítulo 1 Mecanismo de comunicación entre subprocesos

1.1 Mecanismos comunes para la comunicación entre hilos

Qt proporciona una variedad de mecanismos de comunicación entre subprocesos, que incluyen:

  1. Mecanismo de ranura de señal: a través de la conexión de ranura de señal, se puede realizar una comunicación asíncrona entre diferentes subprocesos . El emisor transmite la señal y el receptor recibe la señal a través de la función de ranura y ejecuta la lógica correspondiente. Cuando se usa el mecanismo de ranura de señal, Qt maneja automáticamente la transferencia de datos y la seguridad de subprocesos entre subprocesos .
  2. exec()El método de QThread quit(): al heredar la clase QThread, reescribir run()el método para definir la lógica de ejecución del hilo y luego exec()iniciar el ciclo de eventos del hilo llamando al método. Termina el bucle de eventos del subproceso llamando quit()a un método.
  3. finished()Señales y métodos de QThread QThread::wait(): Cuando un objeto QThread termina de ejecutarse, emite finished()una señal. Puede realizar la espera sincrónica de subprocesos conectando esta señal.
  4. Marco QtConcurrent: Qt proporciona un marco QtConcurrent para simplificar la programación de procesamiento paralelo y tareas de subprocesos múltiples. Los métodos se pueden usar QtConcurrent::run()para ejecutar funciones o expresiones lambda en un nuevo hilo. También puede usar QFuturelas QFutureWatcherclases y para administrar y escuchar los resultados de las tareas asincrónicas.
  5. Eventos y colas de eventos: puede usar el mecanismo de eventos de Qt para enviar eventos personalizados de un hilo a otro. Al personalizar la clase de evento y reescribir event()el método del objeto receptor, se puede realizar la entrega de eventos y el procesamiento personalizado entre subprocesos.
  6. Bloqueos y datos compartidos: a través del mecanismo de bloqueo y datos compartidos de Qt, se puede compartir y proteger datos entre subprocesos. Por ejemplo, puede usar QMutexla clase y QMutexLockerpara implementar una exclusión mutua para evitar que varios subprocesos accedan a un recurso compartido al mismo tiempo.

De acuerdo con las necesidades y la complejidad reales, puede elegir un mecanismo de comunicación entre subprocesos adecuado.

Para una comunicación simple, el mecanismo de ranura de señal suele ser el enfoque más común .

Para casos más complejos, se pueden elegir otros mecanismos para satisfacer las necesidades.

Nota Cuando utilice cualquier mecanismo de comunicación entre subprocesos, preste atención a la seguridad de los subprocesos y la coherencia de los datos.

Observación:

Señales y tragamonedas:

(1) Puede abarcar subprocesos

(2) puede transmitir la señal

(3) Puede transportar cualquier dato de objeto al transmitir la señal

1.2 La solución al desequilibrio entre el hilo de datos de envío y el hilo de datos de recepción

Cuando hay un desequilibrio entre el hilo de datos de envío y el hilo de datos de recepción, pueden producirse pérdidas de datos o retrasos en el procesamiento.

Para resolver este problema, puede considerar los siguientes métodos:

  1. Usar búfer: agregue un búfer de cola entre el hilo de datos de envío y el hilo de datos de recepción para almacenar temporalmente los datos que se enviarán o recibirán. Esto evita el envío frecuente de datos por parte del remitente o el procesamiento frecuente de datos por parte del receptor. Los búferes se pueden implementar utilizando contenedores como QQueueo QListy garantizar la sincronización entre el remitente y el receptor. El remitente puede agregar datos al búfer y el receptor lee los datos del búfer para su procesamiento.

  2. Ajuste la frecuencia de envío: si la tasa de envío de datos es mucho más alta que la tasa de recepción de datos, considere reducir la frecuencia de envío para evitar la sobrecarga de datos. La frecuencia de envío se puede controlar agregando un retraso apropiado entre el envío de datos o usando un temporizador .

  3. Use el grupo de subprocesos: si el subproceso de envío de datos está demasiado ocupado, considere usar el grupo de subprocesos para manejar las tareas de envío. Al asignar los datos que se enviarán a subprocesos inactivos en el grupo de subprocesos, la carga de trabajo se puede equilibrar y se controla la velocidad a la que se envían los datos.

  4. Compresión o filtrado de datos: si la cantidad de datos es demasiado grande o la velocidad de procesamiento de datos no puede mantenerse, puede considerar comprimir o filtrar los datos para reducir la cantidad de datos o mejorar la eficiencia del procesamiento. Se pueden usar algoritmos de compresión para comprimir datos, o se pueden usar reglas o filtros para filtrar datos, y solo se pueden seleccionar los datos requeridos para transmisión o procesamiento.

  5. Optimice la lógica de procesamiento de datos: verifique la lógica de procesamiento del subproceso de datos de recepción para asegurarse de que puedan procesar de manera eficiente los datos recibidos. Puede ser necesario optimizar los algoritmos y evitar cálculos innecesarios u operaciones de E/S para aumentar la velocidad de procesamiento.

A través del método anterior, el problema de desequilibrio entre el hilo de datos de envío y el hilo de datos de recepción se puede resolver para garantizar que los datos se puedan transmitir y procesar a tiempo. La elección del método específico depende del escenario y las necesidades de su aplicación.

Capítulo 2 Comunicación entre objetos : comunicación de señales y ranuras

En Qt, la comunicación de objetos se puede lograr a través del mecanismo de señal y ranura .

Los siguientes son los pasos generales para la comunicación de objetos:

  1. Crear hilos de trabajo y principales.
  2. Defina la señal en el subproceso de trabajo.
  3. Defina la función de ranura en el hilo principal.
  4. Conecte la señal a la función de ranura.
  5. Emita una señal en el subproceso de trabajo para activar la ejecución de la función de ranura.

Aquí hay un ejemplo simple que muestra cómo comunicarse entre dos objetos (dos objetos dentro de un hilo):

// MyWorker.h

#ifndef MYWORKER_H
#define MYWORKER_H

#include <QObject>

class MyWorker : public QObject {
    Q_OBJECT

public:
    explicit MyWorker(QObject *parent = nullptr);

public slots:
    void doWork();

signals:
    void resultReady(int result);
};

#endif // MYWORKER_H


// MyWorker.cpp

#include "MyWorker.h"

MyWorker::MyWorker(QObject *parent) : QObject(parent) {}

void MyWorker::doWork() {
    // 执行耗时操作
    int result = 42;

    // 在工作线程中发射信号,将结果发送到主线程
    emit resultReady(result);
}


// MainWindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

public slots:
    void handleResult(int result);

private slots:
    void startWorker();

private:
    MyWorker *worker;
};

#endif // MAINWINDOW_H


// MainWindow.cpp

#include "MainWindow.h"
#include "MyWorker.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent),
      worker(new MyWorker(this))
{
    connect(worker, &MyWorker::resultReady, this, &MainWindow::handleResult);
    connect(this, &MainWindow::startWorker, worker, &MyWorker::doWork);
    
    // 开始工作线程
    emit startWorker();
}

MainWindow::~MainWindow() {
    delete worker;
}

void MainWindow::handleResult(int result) {
    qDebug() << "Result:" << result;
}

void MainWindow::startWorker() {
    // 开始工作线程
    emit startWorker();
}

En el ejemplo anterior, MyWorkeres una clase de subproceso de trabajo, que hereda de QObject y define funciones resultReadyde señal y doWorkranura en él. doWorkDespués de que la función realiza una operación que requiere mucho tiempo, emitemite resultReadyuna señal a través de la palabra clave y envía el resultado al subproceso principal.

MainWindowEs una clase del hilo principal, que hereda de QMainWindow. En el constructor, conecte la señal a la función de ranura a través de connectla función . De esta manera, la función se activará para ejecutarse después de recibir la señal emitida por el subproceso de trabajo .MyWorkerresultReadyMainWindowhandleResulthandleResult

En MainWindowel constructor de , inicie el subproceso de trabajo mediante emitla palabra clave emit startWorkersignal. De esta forma, doWorkse ejecutará la función en el hilo de trabajo y resultReadyse emitirá la señal.

Cabe señalar que el mecanismo de señal y ranura puede cruzar los límites de los hilos y comunicarse entre diferentes hilos.

Mediante el uso de conexiones de señal y ranura apropiadas, se pueden lograr la transferencia de datos y las operaciones interactivas entre subprocesos.

Además, debe prestar atención a los siguientes puntos al comunicarse entre subprocesos:

  • Ciclo de vida de los objetos: garantiza que los objetos sean válidos mientras dure la conexión y que se gestionen y liberen correctamente cuando ya no se necesiten.
  • Seguridad de subprocesos: todos los datos compartidos a los que se acceda en diferentes subprocesos deben utilizar métodos de sincronización adecuados para garantizar la seguridad de los subprocesos.
  • Bucle de eventos Qt: si está realizando operaciones que consumen mucho tiempo en subprocesos de trabajo, puede usar QCoreApplication::processEvents()esto para asegurarse de que el bucle de eventos continúe procesando otros mensajes para mantener la capacidad de respuesta de su aplicación.

Capítulo 3 Comunicación entre subprocesos: transferencia de datos entre subprocesos

De forma predeterminada, dos objetos que se comunican a través de la comunicación de señales y ranuras están en el mismo subproceso.

¡QT también admite la comunicación entre diferentes objetos de subprocesos a través de señales y ranuras! ! !

En Qt, el paso de datos entre subprocesos se puede lograr a través del mecanismo de señal y ranura .

Aquí hay una forma común:

  1. Cree un hilo emisor y un hilo receptor .
  2. Defina la señal en el subproceso del remitente y agregue el tipo de datos que se pasará en el parámetro de la señal .
  3. Defina la función de ranura en el subproceso para recibir la señal y procesar los datos.
  4. Conecte la señal a la función de ranura.

Aquí hay un ejemplo simple que muestra cómo pasar datos entre hilos:

// WorkerThread.h

#ifndef WORKERTHREAD_H
#define WORKERTHREAD_H

#include <QThread>


// 发送数据线程:先接收数据,然后发送数据ready通知
class WorkerThread : public QThread {
    Q_OBJECT

signals:
    // 定义带参数的信号,用于传递数据
    void dataReady(QString data);

protected:
    void run() override;
};

#endif // WORKERTHREAD_H


// WorkerThread.cpp

#include "WorkerThread.h"

void WorkerThread::run() {
    // 模拟耗时操作
    QThread::sleep(2);

    // 发射信号,传递数据
    emit dataReady("Hello from worker thread!");
}


// MainWindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>


// 在main线程中,通过主窗口对象的槽函数中接收数据
class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

public slots:
    // 定义槽函数,用于接收信号传递的数据
    void handleData(QString data);

private:
    WorkerThread *workerThread;
};

#endif // MAINWINDOW_H


// MainWindow.cpp

#include "MainWindow.h"
#include "WorkerThread.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent),
      workerThread(new WorkerThread())
{
    // 将信号连接到槽函数
    // 发送信号的数据格式与槽函数接收数据,具有相同的代表数据的参数!!!
    connect(workerThread, &WorkerThread::dataReady, this, &MainWindow::handleData);

    // 启动工作线程:接收数据,并把接收到的数据交给主线程的mainwindows对象的槽函数处理
    workerThread->start();
}

MainWindow::~MainWindow() {
    delete workerThread;
}

void MainWindow::handleData(QString data) {
    // 处理接收到的数据
    qDebug() << "Received data:" << data;
}

En el ejemplo anterior, WorkerThreades una clase de trabajador que hereda de QThread. WorkerThreadLas señales se definen en , dataReadyque se utilizan para pasar datos. En runel método, se simula la operación que requiere mucho tiempo y la señal emitse emite a través de la palabra clave dataReady, que transporta un QStringtipo de datos.

MainWindowEs una clase del hilo principal.En el constructor, la señal WorkerThreaddel objeto está conectada a la función de ranura para recibir los datos pasados ​​por la señal. Luego, inicie el subproceso de trabajo llamando al método .dataReadyhandleDataworkerThreadstart

Cuando un subproceso de trabajo termina de realizar una operación que consume mucho tiempo y emite dataReadyuna señal, la señal activa una función de ranura MainWindowen la clase handleData. En la función de ranura, puede procesar los datos recibidos y realizar las operaciones correspondientes.

Cabe señalar que cuando se transfieren datos entre subprocesos, es necesario garantizar la legalidad y la seguridad de los subprocesos de los datos . Al mismo tiempo, dado que el mecanismo de señal y ranura de Qt se basa en el bucle de eventos, cuando se usan subprocesos para la transferencia de datos, puede ser necesario manejar el bucle de eventos para responder a tiempo y manejar la conexión de la ranura de señal.

Supongo que te gusta

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