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

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. 记一次Redis+Getshell经验分享

    前言: 当我们接到一个授权渗透测试的时候,常规漏洞如注入.文件上传等尝试无果后,扫描端口可能会发现意外收获. 知己知彼乃百战不殆,Redis介绍: 简单来说 redis 就是一个Key-Value类型 ...

  2. SQL语句利用日志写shell拿权限

    outfile被禁止,或者写入文件被拦截: 在数据库中操作如下:(必须是root权限) show variables like '%general%'; #查看配置 set global genera ...

  3. 最小生成树的两种方法(Kruskal算法和Prim算法)

    关于图的几个概念定义: 连通图:在无向图中,若任意两个顶点vivi与vjvj都有路径相通,则称该无向图为连通图. 强连通图:在有向图中,若任意两个顶点vivi与vjvj都有路径相通,则称该有向图为强连 ...

  4. 010-PHP输出数组中第某个元素

    <?php $monthName = array(1 => "January", "February", "March",//初 ...

  5. Web application architecture overview

  6. Card Stacking 队列模拟

    题目链接:https://ac.nowcoder.com/acm/contest/993/ABessie is playing a card game with her N-1 (2 <= N ...

  7. GLConsole的编译和使用

    1.在CVars文件中搜索tinyxml,去掉所有tinyxml的编译选项 2.Add the following line to the "CMakeLists.txt" fil ...

  8. GDI+应用

    GDI+:Graphics Device Interface Plus也就是图形设备接口,提供了各种丰富的图形图像处理功能;在C#.NET中,使用GDI+处理二维(2D)的图形和图像,使用Direct ...

  9. 吴裕雄--天生自然C++语言学习笔记:C++ 实例

    C++ 实例 - 输出 "Hello, World!" #include <iostream> using namespace std; int main() { co ...

  10. Ubantu学习笔记3

    修改PATH环境变量 ~/.profile --->(在文件最后一行添加) PATH="要增加的路径:$PATH" 或者 export 使用变量="调用参数&quo ...