近期在解析ts流文件时发现数据量太大,如果在主线程处理会造成界面卡顿,所以需要在QT中加入多线程完成解析数据工作。
通常QT内加入多线程有三种比较方便的方式:
*****分割线------------------------------------
1.moveToThread
新建一个继承QObject a,然后将耗时的工作函数void dowork()
写到这类里面,在主线程new一个Qthread b,再利用
a->moveToThread(b)
移动任务后再启动线程b.start().
通过设置信号与槽的关系触发void dowork()
connect(b, SIGNAL(started()), a, SLOT(dowork())); connect(b, SIGNAL(finished()), a, SLOT(deleteLater()));
完成触发后需要保证安全退出,第二步已经在得到结束信号后触发析构
disconnect(b, SIGNAL(started()), a, SLOT(doWork()));//断开信号和槽函数联系 disconnect(b, SIGNAL(finished()), a, SLOT(deleteLater())); if (b)//退出线程释放内存 { if (b->isRunning()) { b->quit(); b->wait(); } delete b; b = NULL; } if (a)//释放内存 { delete a; a = NULL; }
*****分割线------------------------------------
2.QThreadPool
利用QT自带的线程池也很方便,我们需要自己定义一个Task类继承QRunnable
头文件
#ifndef TASK_H #define TASK_H #include <QRunnable> class Task : public QRunnable { public: Task(); ~Task(); void run() Q_DECL_OVERRIDE; }; #endif // TASK_H
实现
#include "task.h" #include <QDebug> #include <QThread> #include <QDateTime> #include <iostream> #include <fstream> #include <QFile> Task::Task(QString filename) :m_filename(filename) { } Task::~Task() { qDebug().noquote() << QString("~Task() with ID %1").arg(m_id); // 方便查看对象是否被 delete } void Task::run() { //do you want //QMetaObject::invokeMethod(m_pObj,"get_right_data",Q_ARG(QString,temp),Q_ARG(int,m_id)); }
然后在主程序中通过以下开启多线程
QThreadPool *Q_pool = new QThreadPool(); Q_pool->setMaxThreadCount(1);//设置最大线程数目 Task *task = new Task();//构造任务 Q_pool->globalInstance()->start(task);//把任务加入到线程池
此处值得一提的是可以利用该函数和主函数通信传递处理结果
QMetaObject::invokeMethod(m_pObj,"get_right_data",Q_ARG(QString,temp));
m_pObj:主线程对象
get_right_data:主线程接受结果的处理函数
Q_ARG(QString,temp):传递的变量类型和变量名
需要在主线程中实现该函数
Q_INVOKABLE void get_right_data(QString msg);
*****分割线------------------------------------
3.QtConcurrent::run
最简单的方式
首先在.pro文件添加
QT += concurrent
然后添加好头文件
以下是调用的几种方式
MyTest mc; //QFuture后的<>内写入函数返回的类型 //无参数返回,传递参数 QString str = "gg"; QFuture<void> res = QtConcurrent::run(&mc, &MyTest::fun1,str); //有参数返回,无参数传递 QFuture<bool> res1 = QtConcurrent::run(&mc, &MyTest::fun2); for(int i=0;i<10;i++){ qDebug() << "hello main"; } //获取线程中执行函数返回的结果 bool testres = res1.result(); qDebug() << "res1:" << testres ; //超过5个参数的调用 MyDataInfo datainfo; datainfo.a1 = 1; datainfo.a2 = 2; datainfo.a3 = 3; datainfo.a4 = 4; QFuture<void> res3 = QtConcurrent::run(&mc, &MyTest::fun3,str,datainfo); if(!res.isFinished()){判断线程是否执行完成 res.waitForFinished();//等待线程执行完 } qDebug() << "res:" << res.isFinished();//true
*****分割线------------------------------------
心得:
在QT中使用多线程是很有必要的,和大多数界面设计一样,主线程负责UI的构建和更新,所以当你的界面出现卡顿时很有必要加入多线程。
我在进行数据解析的时候有比较耗时的数据转换操作,但是开始并没有想清楚自己线程的具体逻辑,我采取每次需要转换的时候new一个Task,将需要处理的数据通过指针传递到该线程,但是发现处理结果和数据标记号对应不上,我猜测是主线程在某个时间节点传递指针进来后开启了新线程,但是主线程继续读取其他数据,导致指针所指数据区域变化,导致子线程那边处理结果对应不上。我采用在线程被构造时马上就通过memcpy拷贝传递的指针过来也不行,还是会造成数据和数据标记号对应不上,最后采取的办法是开一个新的线程专门处理数据内容,而不是反复与主线程进行指针传递。
希望有理解原因的告知。
全部评论
(0) 回帖