为什么要做多线程,说个最简单的道理就是我们不希望在软件处理数据的时候界面处于无法响应的假死状态。有些处理是灰常花时间的,如果把这样的处理放到主线程中执行,就会导致软件一条路走到底,要等到处理完才能接收响应其他操作,这时候就会导致界面假死。

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多线程的更多相关文章

  1. Qt 多线程和网络编程学习

    一,Qt多线程类学习 QThread类,开始一个新的线程就是开始执行重新实现QThread::run(),run()是默认现实调用exec(),QThread::start()开始线程的执行,run( ...

  2. [转] Qt 多线程学习

    Qt 多线程学习 转自:http://www.cnblogs.com/IT-BOY/p/3544220.html 最近的项目上用到了关于多线程的知识,自己也比较感兴趣,所以就拿了那本<C++ G ...

  3. QT多线程笔记

    1.QT多线程涉及到主线程和子线程之间交互大量数据的时候,使用QThread并不方便,因为run()函数本身不能接受任何参数,因此只能通过信号和槽的交互来获取数据,如果只是单方面简单交互数据还过得去, ...

  4. Qt多线程-总结QThread-QThreadPool-QtConcurrent

    版权声明:若无来源注明,Techie亮博客文章均为原创. 转载请以链接形式标明本文标题和地址: 本文标题:Qt多线程-总结QThread-QThreadPool-QtConcurrent     本文 ...

  5. Qt多线程-QtConcurrent并行运算高级API

    版权声明:若无来源注明,Techie亮博客文章均为原创. 转载请以链接形式标明本文标题和地址: 本文标题:Qt多线程-QtConcurrent并行运算高级API     本文地址:http://tec ...

  6. Qt多线程-QThreadPool线程池与QRunnable

    版权声明:若无来源注明,Techie亮博客文章均为原创. 转载请以链接形式标明本文标题和地址: 本文标题:Qt多线程-QThreadPool线程池与QRunnable     本文地址:https:/ ...

  7. Qt多线程-QThread

    版权声明:若无来源注明,Techie亮博客文章均为原创. 转载请以链接形式标明本文标题和地址: 本文标题:Qt多线程-QThread     本文地址:http://techieliang.com/2 ...

  8. Qt 多线程同步与通信

    Qt 多线程同步与通信 1 多线程同步 Qt提供了以下几个类来完成这一点:QMutex.QMutexLocker.QSemphore.QWaitCondition. 当然可能还包含QReadWrite ...

  9. 【QT】 Qt多线程的“那些事”

    目录 一.前言 二.QThread源码浅析 2.1 QThread类的定义源码 2.2 QThread::start()源码 2.3 QThreadPrivate::start()源码 2.4 QTh ...

随机推荐

  1. package报错

    java类的第一行package 包路径 package报错 jdk版本不一致 或者jar包冲突

  2. 065、Java面向对象之定义一个Book类,在主类中使用Book类

    01.代码如下: package TIANPAN; class Book { // 定义一个新的类 String title; // 书的名字 double price; // 书的价格 public ...

  3. Hive Join优化经验

    大表x小表 这里可以利用mapjoin,SparkSQL中也有mapjoin或者使用广播变量能达到同样效果,此处描述HQL // 开启mapjoin并设定map表大小 set hive.auto.co ...

  4. mysql concat与concat_ws区别

    select concat('大','小') as size from 表 查询出结果为:大小 select concat('大',NULL) as size from 表 查询出结果为:null c ...

  5. oracle进入CDB

    第一步:使用sys登陆 CONN sys/change_on_install AS SYSDBA; 第二步:查看现在的容器名称 SHOW con_name; 第三步:改变容器为PDB ALTER SE ...

  6. yolov3测试自己的数据

    yolov3测试自己的数据 前言 上一篇我已经介绍了利用yolov3预训练权重文件(只包含卷积层)并训练 只需要进行如下编译: ./darknet detector train cfg/voc.dat ...

  7. 【转帖】Windows 10版本占比一览:v1903依然最稳定 占比52.6%

    Windows 10版本占比一览:v1903依然最稳定 占比52.6% https://os.51cto.com/art/202002/611452.htm 其实感觉现阶段的win10 与 2015年 ...

  8. 使用css3的Flex布局实现列表展示

    实现效果图如下: 通过css3样式实现(部分代码): .box { display: flex; flex-wrap:wrap; justify-content:space-between; alig ...

  9. AS-PATH(路径属性)路由路径欺骗术

    AS-PATH(路径属性)路由路径欺骗术: ①:抓取感兴趣流量——前缀与访问 ②:创建路由地图 ③:路由地图第一法则——permit 10 ④:在第一法则中,匹配(感兴趣流量) ⑤:设置 路径欺骗术— ...

  10. 十三、SAP中定义变量时赋初始值

    一.代码如下 二.输出如下