[QT Programming Series-26]: Multi-threading mechanism - QT inter-thread communication and thread event queue event queue, QueuedConnection

Table of contents

Chapter 1 Communication Mechanism Between Threads

1.1 Common Mechanisms for Communication Between Threads

1.2 The solution to the imbalance between the sending data thread and the receiving data thread

Chapter 2 Communication Between Objects: Signal and Slot Communication

Chapter 3 Inter-Thread Communication: Transferring Data Between Threads


Chapter 1 Communication Mechanism Between Threads

1.1 Common Mechanisms for Communication Between Threads

Qt provides a variety of inter-thread communication mechanisms, including:

  1. Signal slot mechanism: Through signal slot connection, asynchronous communication can be performed between different threads. The sender transmits the signal, and the receiver receives the signal through the slot function and executes the corresponding logic. When using the signal-slot mechanism, Qt automatically handles data transfer and thread safety between threads .
  2. exec()The method of QThread quit(): By inheriting the QThread class, rewriting run()the method to define the execution logic of the thread, and then exec()starting the event loop of the thread by calling the method. Terminates the thread's event loop by calling quit()a method.
  3. finished()Signals and methods of QThread QThread::wait(): When a QThread object finishes executing, it emits finished()a signal. You can realize the synchronous waiting of threads by connecting this signal.
  4. QtConcurrent framework: Qt provides a QtConcurrent framework for simplifying the programming of parallel processing and multithreaded tasks. Methods can be used QtConcurrent::run()to execute functions or lambda expressions in a new thread. You can also use QFuturethe and QFutureWatcherclasses to manage and listen to the results of asynchronous tasks.
  5. Events and event queues: You can use Qt's event mechanism to send custom events from one thread to another. By customizing the event class and rewriting event()the method of the receiver object, the event delivery and custom processing between threads can be realized.
  6. Shared data and locks: Through Qt's shared data and lock mechanism, data sharing and protection between threads can be realized. For example, you can use QMutexthe and QMutexLockerclass to implement a mutex to prevent multiple threads from accessing a shared resource at the same time.

According to actual needs and complexity, you can choose a suitable inter-thread communication mechanism.

For simple communication, the signal-slot mechanism is usually the most common approach .

For more complex cases, other mechanisms can be chosen to meet the needs.

Note When using any inter-thread communication mechanism, pay attention to thread safety and data consistency.

Remark:

Signals and slots:

(1) Can span threads

(2) can transmit signal

(3) Can carry any object data when transmitting the signal

1.2 The solution to the imbalance between the sending data thread and the receiving data thread

When there is an imbalance between the sending data thread and the receiving data thread, data loss or processing delays may result.

To solve this problem, you can consider the following methods:

  1. Use buffer: add a queue buffer between the sending data thread and the receiving data thread to temporarily store the data to be sent or received. This avoids frequent sending of data by the sender or frequent processing of data by the receiver. Buffers can be implemented using containers such as QQueueor QListand ensure synchronization between sender and receiver. The sender can add data to the buffer, and the receiver reads data from the buffer for processing.

  2. Adjust the sending frequency: If the rate of sending data is much higher than the rate of receiving data, consider reducing the sending frequency to avoid data overload. The sending frequency can be controlled by adding an appropriate delay between sending data or using a timer .

  3. Use thread pool: If the sending data thread is too busy, consider using thread pool to handle sending tasks. By allocating data to be sent to idle threads in the thread pool, the workload can be balanced and the rate at which data is sent is controlled.

  4. Data compression or filtering: If the amount of data is too large or the data processing speed cannot keep up, you can consider compressing or filtering the data to reduce the amount of data or improve processing efficiency. Compression algorithms can be used for data compression, or rules or filters can be used to filter data, and only the required data can be selected for transmission or processing.

  5. Optimize data processing logic: Check the processing logic of the receiving data thread to ensure that they can efficiently process the received data. It may be necessary to optimize algorithms and avoid unnecessary calculations or IO operations to increase processing speed.

Through the above method, the imbalance problem between the sending data thread and the receiving data thread can be solved to ensure that the data can be transmitted and processed on time. The specific method choice depends on your application scenario and needs.

Chapter 2 Communication Between Objects : Signal and Slot Communication

In Qt, object communication can be achieved through the signal and slot mechanism .

The following are the general steps for object communication:

  1. Create worker and main threads.
  2. Define the signal in the worker thread.
  3. Define the slot function in the main thread.
  4. Connect the signal to the slot function.
  5. Emit a signal in the worker thread to trigger the execution of the slot function.

Here's a simple example showing how to communicate between two objects (two objects inside a thread):

// 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();
}

In the above example, MyWorkerit is a worker thread class, which inherits from QObject, and defines resultReadysignal and doWorkslot functions in it. doWorkAfter the function performs a time-consuming operation, emitit emits resultReadya signal through the keyword and sends the result to the main thread.

MainWindowIs a class of the main thread, which inherits from QMainWindow. In the constructor, connect the signal to the slot function via connectthe function . In this way, the function will be triggered to execute after receiving the signal emitted by the worker thread .MyWorkerresultReadyMainWindowhandleResulthandleResult

In MainWindowthe constructor of , start the worker thread through emitthe keyword emit startWorkersignal. In this way, the function in the worker thread doWorkwill be executed and resultReadythe signal will be emitted.

It should be noted that the signal and slot mechanism can cross thread boundaries and communicate between different threads.

By using appropriate signal and slot connections, data transfer and interactive operations between threads can be achieved.

In addition, you need to pay attention to the following points when communicating between threads:

  • Object Lifecycle: Ensures that objects are valid for the duration of the connection and are properly managed and released when no longer needed.
  • Thread Safety: Any shared data accessed in different threads needs to use appropriate synchronization methods to ensure thread safety.
  • Qt event loop: If you are doing time-consuming operations in worker threads, you can use QCoreApplication::processEvents()this to ensure that the event loop continues to process other messages to keep your application responsive.

Chapter 3 Inter-Thread Communication: Transferring Data Between Threads

By default, two objects communicating through signal and slot communication are in the same thread.

QT also supports communication between different thread objects through signals and slots! ! !

In Qt, passing data between threads can be achieved through the signal and slot mechanism .

Here's a common way:

  1. Create a sender thread and a receiver thread .
  2. Define the signal in the sender thread , and add the data type to be passed in the signal parameter .
  3. Define the slot function in the receiver thread to receive the signal and process the data.
  4. Connect the signal to the slot function.

Here is a simple example showing how to pass data between threads:

// 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;
}

In the above example, WorkerThreadis a worker class that inherits from QThread. WorkerThreadSignals are defined in , dataReadywhich are used to pass data. In runthe method, the time-consuming operation is simulated, and the signal emitis emitted through the keyword dataReady, which carries a QStringtype of data.

MainWindowIt is a class of the main thread. In the constructor, the signal WorkerThreadof the object is connected to the slot function in order to receive the data passed by the signal. Then, start the worker thread by calling the method .dataReadyhandleDataworkerThreadstart

When a worker thread finishes performing a time-consuming operation and emits dataReadya signal, the signal triggers a slot function MainWindowin the class handleData. In the slot function, you can process the received data and perform corresponding operations.

It should be noted that when transferring data between threads, the legality and thread safety of the data need to be guaranteed . At the same time, since Qt's signal and slot mechanism is based on the event loop, when using threads for data transfer, it may be necessary to handle the event loop in order to respond in time and handle the signal slot connection.

Guess you like

Origin blog.csdn.net/HiWangWenBing/article/details/131743199