SeetaFaceQt:Qt多线程
为什么要做多线程,说个最简单的道理就是我们不希望在软件处理数据的时候界面处于无法响应的假死状态。有些处理是灰常花时间的,如果把这样的处理放到主线程中执行,就会导致软件一条路走到底,要等到处理完才能接收响应其他操作,这时候就会导致界面假死。
Qt提供了一个方法用于在提醒软件先处理下未处理的事情,QApplication::processEvents(),所以,如果在主线程中处理一些耗时操作,可以试下频繁的调用QApplication::processEvents()来提醒系统去处理那些未处理的事。但是这个方法毕竟事不靠谱的,某些情况还勉强可以处理,如果耗时操作封装在一个函数接口里,而我们只能去调用它的时候,这个方法就灰常无效,最终还是得用上多线程的方法。
1、QThread
这种方法跟C++的Thread操作时类似的,定义一个类继承QThread,然后重写run()方法,操作几乎是跟C++的Thread一样的。不过这种方法我现在用的很少,几乎就没怎么使用了。
所以重点还是说下第二种方法。
2、moveToThread
这种方法真的是很方便的了。操作就是先定义一个继承QObject的类,然后通过QObject的moveToThread()函数将对象移到一个子线程中就可以实现多线程了。这么简单的方法当然是要跟Qt的信号与槽来配合使用啦。
首先,我们定义一个处理子类继承QObject,然后将我们要放到子线程中执行的操作写到一个槽函数中,然后在主线程中需要处理数据的时候就先将数据传入后通过发射信号令其执行,然后子线程执行完之后发送一个信号通知主线程做出响应即可。
class SeetaFaceThread : public QObject
{
Q_OBJECT
public:
SeetaFaceThread(QObject *parent);
~SeetaFaceThread();
public slots:
void detectFaceSlot(void);
}
这样定义好一个线程类即可了,然后使用的时候,就实例化一个SeetaFaceThread对象和一个QThread对象,然后调用moveToThread()函数将其移到该子线程中,然后通过信号槽函数启动线程,这个做法很多教程都说的很仔细了。然后我想说的是另一种做法,把QThread对象在QObject的子类中实现,这样,在实现QObject的子类的时候就不再需要再去定义一个QThread,使得QObject的子类实现起来更加简单,而不需要考虑线程的结束问题。这个时候需要在QObject的子类中实例化一个QThread的对象,并将QObject的子类moveToThread(),然后需要析构函数里将线程释放。具体的实现如下,在头文件里声明一个QThread类:
QThread *m_pThread;
然后在源文件的构造函数里实例化对象:
m_pThread = new QThread(this);
m_pThread->start();
this->moveToThread(m_pThread);
在析构函数里释放线程:
m_pThread->quit();
m_pThread->wait();
delete m_pThread;
m_pThread = NULL;
这样在构造QObject的子类的时候其实就已经将子类移到子线程中了,而且,在释放内存的时候也会停止子线程并回收资源。然后在使用的时候,直接对QObject的子类进行实例化,然后记得要构造一些信号,传递会主线程,好让主线程知道处理的进度:
m_pSeetaFaceThread = new SeetaFaceThread(this);
connect(m_pSeetaFaceThread,
SIGNAL(faceDetectedFinishedSig(bool)),
this,
SLOT(faceDetectedFinishedSlot(bool)));
connect(m_pSeetaFaceThread,
SIGNAL(faceRecognizerFinishedSig(QStringList)),
this,
SLOT(faceRecognizerFinishedSlot(QStringList)));
connect(m_pSeetaFaceThread,
SIGNAL(extractingInforSig(QString)),
this,
SLOT(extractingInforSlot(QString)));
上面我在子类里定义了三个信号,主要是要通知主线程处理的结果或者一些处理信息。
然后什么时候或者怎么启动线程呢。一般设定一个信号连接要进行处理的槽函数,同时,要进行处理,应该要让子类得到处理的数据吧,一般时可以用信号槽来传递一些变量的,但是这里不太推荐,因为有些数据其实时复杂变量,信号槽有时候时不起作用的,所以,我一般是在子类里添加一些共有成员方法用来传递数据的,传递数据之后使用QTimer::singleShot()函数来作为信号源,并传递给对应槽函数。QTimer::singleShot()函数的原型有几个,主要用的是下面这个:
static void singleShot(int msec, const QObject *receiver, const char *member);
第一个变量是毫秒时间,一般设置为0就是马上传递信号,后面两位是接收器和槽函数,也就是QObject的子类和对应的槽函数。用法类似这样:
m_pSeetaFaceThread->setFileInfoList(filelist);
QTimer::singleShot(0, m_pSeetaFaceThread, SLOT(extractFeaturesSlot(void)));
这里掐掉一些东西后,固定的套路使用如下:
seetafacethread.h:
#ifndef SEETAFACETHREAD_H
#define SEETAFACETHREAD_H
#include <QObject>
class SeetaFaceThread : public QObject
{
Q_OBJECT
public:
SeetaFaceThread(QObject *parent);
~SeetaFaceThread();
signals:
void faceDetectedFinishedSig(bool);
void faceRecognizerFinishedSig(QStringList);
void extractingInforSig(QString);
public slots:
void detectFaceSlot(void);
void alignmentFaceSlot(void);
void extractFeaturesSlot(void);
void recognizerFaceSlot(void);
private:
QThread *m_pThread;
};
#endif // SEETAFACETHREAD_H
seetafacethread.cpp:
#include "seetafacethread.h"
#include <QThread>
SeetaFaceThread::SeetaFaceThread(QObject *parent)
: QObject(parent)
{
m_pThread = new QThread(this);
m_pThread->start();
this->moveToThread(m_pThread);
}
SeetaFaceThread::~SeetaFaceThread()
{
m_pThread->quit();
m_pThread->wait();
delete m_pThread;
m_pThread = NULL;
}
void SeetaFaceThread::detectFaceSlot(void)
{
//一些处理
//处理完成
emit faceDetectedFinishedSig(true);
}
void SeetaFaceThread::alignmentFaceSlot(void)
{
//一些处理
//处理完成
emit faceDetectedFinishedSig(true);
}
void SeetaFaceThread::extractFeaturesSlot(void)
{
//一些处理
//处理完成
emit extractingInforSig(QString::fromLocal8Bit("完成全部特征提取"));
emit extractingInforSig(QString::fromLocal8Bit(""));
}
void SeetaFaceThread::recognizerFaceSlot(void)
{
//一些处理
//处理完成
emit faceRecognizerFinishedSig(strList);
}
然后在需要处理数据的地方调用QTimer::singleShot()函数来执行即可。
Qt多线程还有另外两种做法:继承QRunnable和使用QtConcurrent::run,这个还是可以参考这篇博客的,但是,这两种方法,应该说QtConcurrent::run会有吸引力一些,查了一下,Qt助手的解释是:“The Qt Concurrent module extends the basic threading support found in Qt Core module and simplifies the development of code that can be executed in parallel on all available CPU cores.”也就是说,QtConcurrent扩展了Qt Core中的线程操作,简化了可在所有可用CPU内核上并行执行的代码开发。后面在对它进行深入研究吧。
你站在桥上看风景,
看风景的人在楼上看你。
明月装饰了你的窗子,
你装饰了别人的梦。
--卞之琳 《断章》
SeetaFaceQt:Qt多线程的更多相关文章
- Qt 多线程和网络编程学习
一,Qt多线程类学习 QThread类,开始一个新的线程就是开始执行重新实现QThread::run(),run()是默认现实调用exec(),QThread::start()开始线程的执行,run( ...
- [转] Qt 多线程学习
Qt 多线程学习 转自:http://www.cnblogs.com/IT-BOY/p/3544220.html 最近的项目上用到了关于多线程的知识,自己也比较感兴趣,所以就拿了那本<C++ G ...
- QT多线程笔记
1.QT多线程涉及到主线程和子线程之间交互大量数据的时候,使用QThread并不方便,因为run()函数本身不能接受任何参数,因此只能通过信号和槽的交互来获取数据,如果只是单方面简单交互数据还过得去, ...
- Qt多线程-总结QThread-QThreadPool-QtConcurrent
版权声明:若无来源注明,Techie亮博客文章均为原创. 转载请以链接形式标明本文标题和地址: 本文标题:Qt多线程-总结QThread-QThreadPool-QtConcurrent 本文 ...
- Qt多线程-QtConcurrent并行运算高级API
版权声明:若无来源注明,Techie亮博客文章均为原创. 转载请以链接形式标明本文标题和地址: 本文标题:Qt多线程-QtConcurrent并行运算高级API 本文地址:http://tec ...
- Qt多线程-QThreadPool线程池与QRunnable
版权声明:若无来源注明,Techie亮博客文章均为原创. 转载请以链接形式标明本文标题和地址: 本文标题:Qt多线程-QThreadPool线程池与QRunnable 本文地址:https:/ ...
- Qt多线程-QThread
版权声明:若无来源注明,Techie亮博客文章均为原创. 转载请以链接形式标明本文标题和地址: 本文标题:Qt多线程-QThread 本文地址:http://techieliang.com/2 ...
- Qt 多线程同步与通信
Qt 多线程同步与通信 1 多线程同步 Qt提供了以下几个类来完成这一点:QMutex.QMutexLocker.QSemphore.QWaitCondition. 当然可能还包含QReadWrite ...
- 【QT】 Qt多线程的“那些事”
目录 一.前言 二.QThread源码浅析 2.1 QThread类的定义源码 2.2 QThread::start()源码 2.3 QThreadPrivate::start()源码 2.4 QTh ...
随机推荐
- other#一些问题的列表
centos7及以后修改hostname, hostnamectl set-hostname centos7 centos7之前修改hostname, vi /etc/sysconfig/networ ...
- HDU3306-Another kind of Fibonacci(矩阵构造)
Another kind of Fibonacci Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Jav ...
- PHP中strlen和mb_strlen函数的使用方式的不同
(1)strlen 获取字符串长度 (2)mb_strlen 使用方法: int mb_strlen ( string $str [, string $encoding ] )返回给定的字符串 ...
- spring core:@AliasFor的派生性
spring对Annotation的派生性应用可谓炉火纯青,在spring core:@Component的派生性讲过支持层次上派生性,而属性上派生的需求则借助了@AliasFor,它是从spring ...
- javascript中call与this的初见
call定义 语法:call([thisObj[,arg1[, arg2[, [,.argN]]]]]) 定义:调用一个对象的一个方法,以另一个对象替换当前对象. 说明:call 方法可以用来代替另一 ...
- 《ES6标准入门》(阮一峰)--5.字符串的新增方法
1.String.fromCodePoint() ES5 提供String.fromCharCode()方法,用于从 Unicode 码点返回对应字符,但是这个方法不能识别码点大于0xFFFF的字符. ...
- POJ1723 SOLDIERS 兄弟连
SOLDIERS 有一个性质:在一个长为n的序列a中找一个数 \(a_k\) 使得 \(\sum\limits_{i=1}^n abs(a_i-a_k)\) 最小,则 \(a_k\) 是a的中位数. ...
- 矿难已经过去,NVIDIA将高举光追大旗!
在去年8月推出支持光线追踪技术的Turing图灵GPU之前,显卡市场差不多沉沦了两年,很大一个原因就是2017到2018年初的数字货币市场爆发,矿卡市场取代了游戏卡市场成为增长点,那一年多显卡厂商的日 ...
- C++面试常见问题——07容器和迭代器
容器和迭代器 vector.list.deque #include<iostream> #include<vector> #include<deque> #incl ...
- docker 安装好后启动异常解决
一个月前在虚拟机中根据视频教程安装了docker 启动docker后执行 systemctl status docker 出现了异常,具体如下: [root@joinApp2 ~]# systemct ...