Qt多线程学习(二)——使用QObject

目录

  • 目录
  • 前言
  • 多线程
    • 多线程继承QObject
      • 继承QObject的多线程实现
      • 代码实现
  • 总结
  • 参考链接

前言

在运行Qt程序时遇到一个问题:在没有鼠标或触摸屏操作的时候,Qt界面显示USB摄像头的视频很流畅。但是一旦有鼠标操作或者触摸屏操作的时候,就会出现Qt视频流停止运行的现象;而且只有鼠标或者触摸屏有事件发生的时候,Qt界面视频流才会一帧一帧的运行。感觉很奇怪!先试试多线程看看吧!
本文参考Qt使用多线程的一些心得——2.继承QObject的多线程使用方法

2018.1.26补充:在学习了继承使用QObject来实现多线程之后,一直频繁的使用,在这过程中,经常遇到这个问题:在主线程中创建多线程,然后主线程假死。
原因很简单:在主线程中创建了线程对象,在线程对象的构造函数中调用了线程中消耗CPU时间的函数,而构造函数是在主线程中运行的,所以调用的函数其实还是在主线程中运行,结果计算量过大的函数就导致主线程的事件循环进入假死状态。
解决方法就是:在主线程中创建线程对象,但是不要在构造函数中调用耗时函数,如果调用,其实还是在主线程的事件循环当中。我们应该在主线程中使用一个信号,来触发子线程中的耗时函数。

多线程

多线程继承QObject

QObjectQt框架的基本类,但凡涉及到信号与槽有关的类都是继承于QObjectQObject提供了Qt关键技术信号与槽的支持以及事件系统的支持,同时它提供了线程操作的接口,也就是QObject可以选择在不同的线程中执行。
QObject的线程转移函数是:void moveToThread(QThread* targetThread),通过此函数可以把一个顶层Object(就是没有父级)转移到一个新的线程中。

继承QObject的多线程实现

QObject来实现多线程默认支持事件循环(Qt的许多非GUI类也需要事件循环支持,如QTimerQTcpSocket),QThread要支持事件循环需要在QThread::run()中调用QThread::exec(),否则那些需要事件循环支持的类都不能正常发送信号,因此如果要使用信号与槽,直接使用QObject来实现多线程。

QObject的线程转移函数:void moveToThread(QThread* targetThread),通过此函数可以把一个顶层QObject转移到一个新线程里。
QThread自身并不生存在它run函数所在的线程,而是生存在旧的线程中。

  • 创建及销毁线程
    继承QObject多线程的方法创建线程要让QThreadstart函数运行起来,但是需要注意销毁线程的方法。在线程创建之后,QObject的销毁不应该在主线程中进行,而是通过deleteLater槽进行安全的销毁。因为,继承QObject多线程的方法在创建时有几个槽函数需要特别注意:
    1) QThreadfinished信号对接QObjectdeleteLater使得线程结束后,继承QObject的那个多线程类会自己销毁。
    2) QThreadfinished信号对接QThread自己的deleteLater

使用QObject创建多线程的方法如下:

  • 写一个继承QObject的类,对需要进行复杂耗时逻辑的入口函数声明为槽函数。
  • 此类在旧线程new出来,不能给它设置任何父对象
  • 同时声明一个QThread,如果不是new,在析构时需要调用QThread::wait(),如果是堆分配的话,可以通过deleteLater让线程自动销毁。
  • obj通过moveToThread方法转移到新线程中,此时object已经在线程中。
  • 把线程的finished信号和objectdeleteLater槽连接,这个信号槽必须连接,否则会内存泄漏。
  • 正常连接其他信号和槽
  • 初始化完后调用QThread::start()来启动线程
  • 在逻辑结束后,调用QThread::quit退出线程的事件循环
    使用QObject来实现多线程比用继承QThread的方法更加灵活,整个类都是在新的线程中,通过信号槽和主线程传递数据。

代码实现

  • ThreadObject.h
#ifndef THREADOBJECT_H
#define THREADOBJECT_H

#include 
#include 

class ThreadObject : public QObject
{
    Q_OBJECT
public:
    ThreadObject(QObject* parent = NULL);
    ~ThreadObject();
    void setRunCount(int count);
    void stop();

signals:
    void message(const QString& info);
    void progress(int present);

public slots:
    void runSomeBigWork1();
    void runSomeBigWork2();

private:
    int m_runCount;
    int m_runCount2;
    bool m_isStop;

    QMutex m_stopMutex;
};

#endif // THREADOBJECT_H
  • ThreadObject.cpp
#include "ThreadObject.h"
#include 
#include 
#include 
#include 
#include 
#include 

ThreadObject::ThreadObject(QObject *parent):QObject(parent)
  ,m_runCount(10)
  ,m_runCount2(std::numeric_limits<int>::max())
  ,m_isStop(true)
{

}

ThreadObject::~ThreadObject()
{
    qDebug() << "ThreadObject destroy";
    emit message(QString("Destroy %1->%2,thread id:%3").arg(__FUNCTION__).arg(__FILE__).arg(*(int*)QThread::currentThreadId()));
}

void ThreadObject::setRunCount(int count)
{
    m_runCount = count;
    emit message(QString("%1->%2,thread id:%3").arg(__FUNCTION__).arg(__FILE__).arg(*(int*)QThread::currentThreadId()));
}

void ThreadObject::runSomeBigWork1()
{
    {
        QMutexLocker locker(&m_stopMutex);
        m_isStop = false;
    }

    int count = 0;

    QString str = QString("%1->%2,thread id:%3").arg(__FILE__).arg(__FUNCTION__).arg(*(int*)QThread::currentThreadId());
    emit message(str);

    int process = 0;

    while(1)
    {
        {
            QMutexLocker locker(&m_stopMutex);
            if(m_isStop)
                return;
        }

        if(m_runCount == count)
        {
            break;
        }

        sleep(1);
        int pro = ((float)count / m_runCount) * 100;

        if(pro != process)
        {
            process = pro;
            emit progress(((float)count / m_runCount) * 100);
            emit message(QString("Object::run times:%1,m_runCount:%2").arg(count).arg(m_runCount2));
        }

        ++count;
    }
}

void ThreadObject::runSomeBigWork2()
{
    {
        QMutexLocker locker(&m_stopMutex);
        m_isStop = false;
    }

    int count = 0;

    QString str = QString("%1->%2,thread id:%3").arg(__FILE__).arg(__FUNCTION__).arg(*(int*)QThread::currentThreadId());
    emit message(str);

    int process = 0;
    QElapsedTimer timer;
    timer.start();

    while(1)
    {
        {
            QMutexLocker locker(&m_stopMutex);
            if(m_isStop)
                return;
        }

        if(m_runCount2 == count)
        {
            break;
        }

        int pro = ((float)count / m_runCount2) * 100;
        if(pro != process)
        {
            process = pro;
            emit progress(pro);
            emit message(QString("%1,%2,%3,%4")
                         .arg(count)
                         .arg(m_runCount2)
                         .arg(pro)
                         .arg(timer.elapsed()));

            timer.restart();
        }
        ++count;
    }
}

void ThreadObject::stop()
{
    QMutexLocker locker(&m_stopMutex);
    emit message(QString("%1->%2,thread id:%3").arg(__FUNCTION__).arg(__FILE__).arg(*(int*)QThread::currentThreadId()));
    m_isStop = true;
}

可以知道,在这个继承OObject的类中,并没有继承和线程相关的东西,只是一个普通的类而已。但是通过moveToThread,这个类的功能就在一个线程中运行了。

  • widget.cpp
void Widget::startObjThread()
{
    if(m_objThread)
    {
        return;
    }

    m_objThread = new QThread();        // deleteLater make thread kill itself
    m_obj = new ThreadObject();         // new thread inherit from QObject, without parent object
    m_obj->moveToThread(m_objThread);

    connect(m_objThread, &QThread::finished, m_objThread, &QObject::deleteLater);
    connect(m_objThread, &QThread::finished, m_obj, &QObject::deleteLater);
    connect(this, &Widget::startObjThreadWork1, m_obj, &ThreadObject::runSomeBigWork1);
    connect(this, &Widget::startObjThreadWork2, m_obj, &ThreadObject::runSomeBigWork2);
    connect(m_obj, &ThreadObject::progress, this, &Widget::progress);
    connect(m_obj, &ThreadObject::message,  this, &Widget::receiveMessage);

    m_objThread->start();
}

这个函数在某个槽中被调用,然后按照这个流程即可建立一个继承QObject的线程。主要就是

    m_objThread = new QThread();        // deleteLater make thread kill itself
    m_obj = new ThreadObject();         // new thread inherit from QObject, without parent object
    m_obj->moveToThread(m_objThread);

    connect(m_objThread, &QThread::finished, m_objThread, &QObject::deleteLater);
    connect(m_objThread, &QThread::finished, m_obj, &QObject::deleteLater);

总结

使用QThread需要调用QThread::exec(),否则那些需要事件循环支持的类都不能正常发送信号,因此如果要使用信号与槽,直接使用QObject来实现多线程。

参考链接

Qt使用多线程的一些心得——1.继承QThread的多线程使用方法
QThread详解

你可能感兴趣的:(qt,qt)