当前位置: 首页 > news >正文

1. 多线程开发

目录

  • 方法1.继承QThread
    • 使用案例
    • 总结
  • 方法2.将qobject对象moveToThread(官方推荐)
    • 使用案例
    • 总结
  • 方法3.QRunnable + QThreadPool
    • 使用案例
    • 总结
  • 方法4.快速线程QtConcurrent+QFutureWatcher
    • 使用案例
    • 总结

方法1.继承QThread

需要实现QThread的抽象函数run

#ifndef WORKERTHREAD_H
#define WORKERTHREAD_H#include <QObject>
#include <QThread>
#include <QDebug>class WorkerThread : public QThread
{Q_OBJECT
public:explicit WorkerThread(QObject *parent = nullptr);void func1();protected:virtual void run();signals:
};#endif // WORKERTHREAD_H#include "workerthread.h"WorkerThread::WorkerThread(QObject *parent): QThread{parent}
{}void WorkerThread::func1()
{qDebug()<< __FUNCTION__<< QThread::currentThread();
}void WorkerThread::run()
{qDebug()<< __FUNCTION__<< QThread::currentThread();
}

使用案例

#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);qDebug()<< "主线程:" << QThread::currentThread();wt = new WorkerThread(this);wt->start();//执行WorkerThread的run在子线程中wt->func1();    //主线程connect(ui->pushButton,&QPushButton::clicked,wt,&WorkerThread::func1); //主线程}Widget::~Widget()
{delete ui;
}

总结

run函数是在子线程中执行的
其成员函数func1
a)直接调用的方式是在创建对象的线程(主线程)中执行的
b)信号槽连接的方式也是在创建对象的线程(主线程)中执行的

直接调用接口,如果包含了run线程中使用的变量可能就会导致数据竞争
此时就得考虑加锁
如果run线程中获取到了锁,主线程调用WorkerThread的接口会导致阻塞,界面卡顿=
使用场景,不需要进行线程间数据交互

方法2.将qobject对象moveToThread(官方推荐)

#ifndef WORKER_H
#define WORKER_H#include <QObject>class Worker : public QObject
{Q_OBJECT
public:explicit Worker(QObject *parent = nullptr);~Worker();void func1();signals:
};#endif // WORKER_H
#include "worker.h"
#include <QThread>
#include <QDebug>Worker::Worker(QObject *parent): QObject{parent}
{}Worker::~Worker()
{
qDebug()<< __FUNCTION__ << QThread::currentThread();
}void Worker::func1()
{qDebug()<< __FUNCTION__ << QThread::currentThread();
}

使用案例

#ifndef FORM_H
#define FORM_H#include <QWidget>
#include <QThread>
#include <QDebug>#include "worker.h"namespace Ui {
class Form;
}class Form : public QWidget
{Q_OBJECTpublic:explicit Form(QWidget *parent = nullptr);~Form();
signals:void closeThread();private:Ui::Form *ui;
};#endif // FORM_H
#include "form.h"
#include "ui_form.h"Form::Form(QWidget *parent): QWidget(parent), ui(new Ui::Form)
{ui->setupUi(this);//根据亲和性,不应设置为其他线程的成员对象QThread *th = new QThread();Worker *worker = new Worker();qDebug()<< "主线程:" << QThread::currentThread();worker->moveToThread(th);connect(th,&QThread::finished,worker,&Worker::deleteLater);connect(th,&QThread::finished,worker,&QThread::deleteLater);th->start();    //启动子线程worker->func1();    //错误行为connect(ui->pushButton,&QPushButton::clicked,worker,&Worker::func1); //正确线程间通信connect(this,&Form::closeThread,th,&QThread::quit); //关闭线程
}Form::~Form()
{delete ui;emit closeThread();}

总结

使用场景: 线程之间频繁交互的时候(官方推荐)

①禁止行为:禁止直接调用子线程对象接口或者修改数据
因为这会导致跨线程访问问题,可能引发数据竞争、死锁或程序崩溃。
应该通过信号槽连接进行线程间通信。

②独立的事件循环
子线程开启会产生一个独立的事件循环,此时异步线程信号请求会在此次统一处理
不会对发起请求的线程阻塞;

③Qt 的父子对象与线程规则​
​规则 1​:所有 QObject 父子对象必须位于同一线程。
​规则 2​:调用 moveToThread() 会改变对象的线程亲和性(Thread Affinity)。
​冲突点​:如果将一个已移动到子线程的 QObject 设置为另一个线程中对象的子对象,Qt 会触发断言崩溃(如 QObject: Cannot create children for a parent that is in a different thread)。
简单点说既是;A线程中的对象不应该成为B线程的父对象或者子对象

方法3.QRunnable + QThreadPool

#ifndef WORKERTASK_H
#define WORKERTASK_H#include <QRunnable>class WorkerTask : public QRunnable
{
public:WorkerTask();~WorkerTask();
protected:virtual void run();
};#endif // WORKERTASK_H
#include "workertask.h"
#include <QThread>
#include <QDebug>WorkerTask::WorkerTask() {setAutoDelete(true);
}WorkerTask::~WorkerTask()
{
qDebug()<< __FUNCTION__<< QThread::currentThread();
}void WorkerTask::run()
{
qDebug()<< __FUNCTION__<< QThread::currentThread();
}

使用案例

#ifndef FORM1_H
#define FORM1_H#include <QWidget>
#include <QThread>
#include <QDebug>namespace Ui {
class Form1;
}class Form1 : public QWidget
{Q_OBJECTpublic:explicit Form1(QWidget *parent = nullptr);~Form1();private:Ui::Form1 *ui;
};#endif // FORM1_H
#include "form1.h"
#include "ui_form1.h"
#include "workertask.h"
#include <QThreadPool>Form1::Form1(QWidget *parent): QWidget(parent), ui(new Ui::Form1)
{ui->setupUi(this);qDebug()<< "主线程:" << QThread::currentThread();QThreadPool::globalInstance()->start(new WorkerTask());}Form1::~Form1()
{delete ui;
}

总结

场景:适合短生命周期的任务
用完就释放

方法4.快速线程QtConcurrent+QFutureWatcher

其他线程都得创建一堆的对象,管理对象的创建与释放生命周期,
但是QtConcurrent不用,使用QtConcurrent我们可以快速开启一个线程

使用案例

#include "form2.h"
#include "ui_form2.h"
#include <QDebug>
#include <QThread>
#include <QtConcurrent>
#include <QtConcurrent/QtConcurrentMap>Form2::Form2(QWidget *parent): QWidget(parent), ui(new Ui::Form2)
{ui->setupUi(this);qDebug()<< "主线程:" << QThread::currentThread();
}Form2::~Form2()
{delete ui;
}void Form2::on_pushButton_clicked()
{QList<int> list = {1, 2, 3, 4, 5};//通过map开启多个线程执行, 如果使用for每个元素处理是耗时的,但是采用此法可以并行处理// QFuture<void> future = QtConcurrent::map(list, [](int &value) {//     value *= 2; // 将每个元素乘以2//     qDebug()<< __FUNCTION__ << QThread::currentThread();// });QFuture<int> future = QtConcurrent::run([]() -> int {// 计算并返回结果return 42;});qDebug()<< future.result(); //阻塞等待结构
}

绑定监听器,全部执行完则发出fnish信号

QFutureWatcher<void> watcher;
connect(&watcher, &QFutureWatcher<void>::finished, this, []() {qDebug() << "图像处理完成";
});watcher.setFuture(future);

总结

​避免修改共享数据​:尽量使用值传递而非引用传递
​使用轻量级函数​:并行执行的函数应该尽可能轻量
​合理设置线程池大小​:使用 QThreadPool::globalInstance()->setMaxThreadCount()
​处理异常​:确保并行函数不会抛出未捕获的异常

http://www.xdnf.cn/news/1188181.html

相关文章:

  • 【NLP舆情分析】基于python微博舆情分析可视化系统(flask+pandas+echarts) 视频教程 - 热词评论查询功能实现
  • 机器学习(重学版)基础篇(概念与评估)
  • Qt 远程过程调用(RPC)实现方案
  • 大模型应用班-第2课 DeepSeek使用与提示词工程课程重点 学习ollama 安装 用deepseek-r1:1.5b 分析PDF 内容
  • UniappDay03
  • 高斯数据库触发器实现流水号的
  • 去中心化时代的通信革命:briefing与cpolar技术融合带来的安全范式革新
  • 【Linux系统】理解硬件 | 引入文件系统
  • 机器学习特征工程:特征选择及在医学影像领域的应用
  • 数字孪生映射探索驱动的具身导航!MorphoNavi:面向对象映射的空地机器人导航
  • 2D游戏背景滚动教程(JavaSwing)
  • 机器学习特征工程详解:特征选择与降维(PCA)
  • 数据赋能(336)——技术平台——智能化运营
  • 2.JVM跨平台原理(字节码机制)
  • Java研学-RabbitMQ(二)
  • C++中new和delete的多重面孔:operator new、new operator与placement new解析
  • 初识java
  • Python 程序设计讲义(20):选择结构程序设计——双分支结构的简化表示(三元运算符)
  • Model Control Protocol 三层架构设计,三种传输方式,完成MCP项目构建实现工具调试,多维度评价指标检测多工具多资源调用的鲁棒性和稳健性
  • java面试题(二)
  • 栈----1.有效的括号
  • 扒网站工具 HTTrack Website Copier
  • 3020雕刻机脱机自定义指令
  • 一些常见的网络攻击方式
  • 疯狂星期四第19天运营日记
  • Java并发编程第十篇(ThreadPoolExecutor线程池组件分析)
  • 锁相环技术简介(面向储能变流器应用)
  • 机器学习(一)KNN,K近邻算法(K-Nearest Neighbors)
  • [硬件电路-85]:一款高集成度热电制冷器(TEC)控制器芯片ADN8835ACPZ
  • 工程师实践出真知