[QT编程系列-26]:多线程机制 - QT线程间通信与线程的事件队列event queue、QueuedConnection

目录

第1章 线程间通信机制

1.1  线程间通信常用机制

1.2 发送数据线程和接收数据线程之间不平衡的解决之道

第2章 对象间通信:信号与槽通信

第3章 线程间通信:线程间传送数据


第1章 线程间通信机制

1.1  线程间通信常用机制

Qt中提供了多种线程间通信的机制,包括:

  1. 信号槽机制:通过信号槽连接,可以在不同线程之间进行异步通信。发送方通过发射信号,接收方通过槽函数来接收信号并执行相应逻辑。在使用信号槽机制时,Qt会自动处理线程间的数据传输和线程安全
  2. QThread的exec()quit()方法:通过继承QThread类,重写run()方法来定义线程的执行逻辑,然后通过调用exec()方法启动线程的事件循环。通过调用quit()方法来终止线程的事件循环。
  3. QThread的finished()信号和QThread::wait()方法:当一个QThread对象完成执行时,会发射finished()信号。可以通过连接这个信号来实现线程的同步等待。
  4. QtConcurrent framework:Qt提供了一个QtConcurrent框架,用于简化并行处理和多线程任务的编程。可以使用QtConcurrent::run()方法来在新线程中执行函数或Lambda表达式。还可以使用QFutureQFutureWatcher类来管理和监听异步任务的结果。
  5. 事件和事件队列:可以使用Qt的事件机制将自定义事件从一个线程发送到另一个线程。通过自定义事件类和重写接收方对象的event()方法,可以实现线程间的事件传递和自定义处理。
  6. 共享数据和锁:通过Qt的共享数据和锁机制,可以实现线程间的数据共享和保护。例如,可以使用QMutexQMutexLocker类来实现互斥锁,防止多个线程同时访问共享资源。

根据实际需求和复杂性,可以选择适合的线程间通信机制。

对于简单的通信,信号槽机制通常是最常用的方法

对于更复杂的情况,可以选择其他机制来满足需求。

注意在使用任何线程间通信机制时,都要注意线程安全性和数据一致性。

备注:

信号与槽:

(1)可以跨越线程

(2)可以传送信号

(3)可以在传送信号时携带任何对象数据

1.2 发送数据线程和接收数据线程之间不平衡的解决之道

当发送数据线程和接收数据线程之间不平衡时,可能会导致数据丢失或处理延迟。

为了解决这个问题,你可以考虑以下几种方法:

  1. 使用缓冲区:在发送数据线程和接收数据线程之间添加队列缓冲区,用于临时存储待发送或已接收的数据。这样可以避免发送方频繁发送数据或接收方频繁处理数据。可以使用QQueueQList等容器来实现缓冲区,并确保在发送方和接收方之间保持同步。发送方可以将数据添加到缓冲区中,而接收方则从缓冲区中读取数据进行处理。

  2. 调整发送频率:如果发送数据的速率远远高于接收数据的速率,可以考虑降低发送频率,以避免数据过载。可以通过在发送数据之间添加适当的延迟或使用定时器来控制发送频率

  3. 使用线程池:如果发送数据线程过于繁忙,可以考虑使用线程池来处理发送任务。通过将待发送的数据分配给线程池中的空闲线程,可以平衡工作负载,并确保发送数据的速率得到控制。

  4. 进行数据压缩或筛选:如果数据量过大或数据处理速度跟不上,可以考虑对数据进行压缩或筛选,以减少数据量或提高处理效率。可以使用压缩算法进行数据压缩,或使用规则或过滤器对数据进行筛选,只选择需要的数据进行传输或处理。

  5. 优化数据处理逻辑:检查接收数据线程的处理逻辑,确保它们能够高效处理接收到的数据。可能需要优化算法、避免不必要的计算或IO操作,以提高处理速度。

通过以上方法,可以解决发送数据线程和接收数据线程之间的不平衡问题,确保数据能够按时传送和处理。具体的方法选择取决于你的应用场景和需求。

第2章 对象间通信:信号与槽通信

在Qt中,对象通信可以通过信号和槽机制来实现。

以下是对象通信的一般步骤:

  1. 创建工作线程和主线程。
  2. 在工作线程中定义信号。
  3. 在主线程中定义槽函数。
  4. 将信号连接到槽函数。
  5. 在工作线程中发射信号,触发槽函数的执行。

下面是一个简单的示例,展示了如何在两个对象之间进行通信(线程内部的两个对象):

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

在上述示例中,MyWorker是一个工作线程的类,它继承自QObject,并在其中定义了resultReady信号和doWork槽函数。doWork函数执行耗时操作后,通过emit关键字发射resultReady信号,将结果发送到主线程。

MainWindow是一个主线程的类,它继承自QMainWindow。在构造函数中,通过connect函数将MyWorker的信号resultReady连接到MainWindow的槽函数handleResult。这样,在接收到工作线程发射的信号后,handleResult函数将被触发执行。

MainWindow的构造函数中,通过emit关键字发射startWorker信号,开始工作线程。这样,在工作线程中的doWork函数将会被执行,并发射resultReady信号。

需要注意的是,信号和槽机制可以跨越线程边界,在不同的线程之间进行通信。

通过使用合适的信号和槽连接,可以实现线程间的数据传递和交互操作。

此外,在进行线程间通信时需要注意以下几点:

  • 对象的生命周期:确保对象在连接期间是有效的,并且在不再需要时进行适当的管理和释放。
  • 线程安全:任何在不同线程中访问的共享数据都需要使用适当的同步方法来保证线程安全。
  • Qt事件循环:如果在工作线程中进行耗时操作,可以使用QCoreApplication::processEvents()来确保事件循环继续处理其他消息,以保持应用程序的响应性。

第3章 线程间通信:线程间传送数据

默认情况下,通过信号与槽通信通信的两个对象是在同一个线程内。

QT也支持不同线程对象之间通过信号与槽通信!!!

在Qt中,线程间传递数据可以通过信号和槽机制来实现。

下面是一种常见的方式:

  1. 创建发送方线程接收方线程
  2. 发送方线程中定义信号,并在信号参数中添加需要传递的数据类型
  3. 接收方线程中定义槽函数,用于接收信号并处理数据。
  4. 将信号连接到槽函数。

以下是一个简单的示例,展示了如何在线程之间传递数据:

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

在上述示例中,WorkerThread是一个工作线程的类,继承自QThread。在WorkerThread中定义了dataReady信号,用于传递数据。在run方法中,模拟了耗时操作,并通过emit关键字发射了dataReady信号,其中携带了一个QString类型的数据。

MainWindow是一个主线程的类,在构造函数中将WorkerThread对象的dataReady信号连接到handleData槽函数,以便接收信号传递的数据。然后,通过调用workerThreadstart方法启动工作线程。

当工作线程执行完耗时操作并发射dataReady信号时,该信号会触发MainWindow类中的handleData槽函数。在槽函数中,可以处理接收到的数据并进行相应的操作。

需要注意的是,在线程之间传递数据时,需要保证数据的合法性和线程安全。同时,由于Qt的信号和槽机制是基于事件循环的,因此在使用线程进行数据传递时,可能需要处理事件循环以便及时响应和处理信号槽连接。

  • 2
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

文火冰糖的硅基工坊

你的鼓励是我前进的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值