Qt 多线程(继承QObject,调用moveToThread方法)

QObject是Qt框架的基本类,但凡涉及到信号槽有关的类都是继承于QObjectQObject是一个功能异常强大的类,它提供了Qt关键技术信号和槽的支持以及事件系统的支持,同时它提供了线程操作的接口,也就是QObject是可以选择不同的线程里执行的。

QObject的线程转移函数是:void moveToThread(QThread * targetThread) ,通过此函数可以把一个顶层Object(就是没有父级)转移到一个新的线程里。

QThread非常容易被新手误用,主要是QThread自身并不生存在它run函数所在的线程,而是生存在旧的线程中。由于QThread的这个特性,导致在调用QThread的非run函数容易在旧线程中执行。

继承QObject,调用moveToThread方法用的时候要强调的几个重点:
自定义的MyThread线程类的对象在创建时不能指定父对象!
启动子线程后,并没有启动子线程处理函数;
启动子线程处理函数必须用signal-slot方式!!!
在线程处理函数内部,绝对不允许操作图形界面(比如跳个弹窗等等),线程内部通常是纯数据处理!

1.首先,在工程中新建一个MyThread类,继承于QObject
mythread.h

#ifndef MYTHREAD_H
#define MYTHREAD_H

#include <QObject>

class MyThread : public QObject
{
    Q_OBJECT
public:
    explicit MyThread(QObject *parent = nullptr);

    //线程处理函数
    void myTimeout();

    void setFlag(bool flag=true);

    void dealSignal_MyThread();

signals:
    void mySignal();
private:
    bool isStop;

};

#endif // MYTHREAD_H

mythread.cpp

#include "mythread.h"
#include<QThread>
#include<QDebug>
MyThread::MyThread(QObject *parent) : QObject(parent)
{
    isStop=false;//初始化
}
void MyThread::setFlag(bool flag)
{
    isStop=flag;
}

void MyThread::myTimeout()
{
    while(isStop==false)
    {
        QThread::sleep(1);
        emit mySignal();
        qDebug()<<"子线程号:"<<QThread::currentThread();


    }


}
void MyThread::dealSignal_MyThread()
{
    static int i=0;
    i++;
    qDebug()<<i;
    qDebug()<<"子线程号:"<<QThread::currentThread();

}

2.再来看MyWidget类
mywidget.h

#ifndef MYWIDGET_H
#define MYWIDGET_H

#include <QWidget>
#include"mythread.h"
#include<QThread>
QT_BEGIN_NAMESPACE
namespace Ui { class MyWidget; }
QT_END_NAMESPACE

class MyWidget : public QWidget
{
    Q_OBJECT
signals:
    void startThread();//启动子线程处理函数的信号
public:
    MyWidget(QWidget *parent = nullptr);
    ~MyWidget();

private slots:
    void on_ButtonStart_clicked();
    void dealSignal_MyWidget();
    void dealDestroyed();
    void on_ButtonStop_clicked();

private:
    Ui::MyWidget *ui;
    MyThread *my_thread;
    QThread *sub_thread;
};
#endif // MYWIDGET_H

mywidget.cpp

#include "mywidget.h"
#include "ui_mywidget.h"
#include<QDebug>
MyWidget::MyWidget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::MyWidget)
{
    ui->setupUi(this);

    //动态分配空间,不能指定父对象
    my_thread=new MyThread;

    //创建子线程
    sub_thread=new QThread(this);
    //把自定义的线程加入到子线程中
    my_thread->moveToThread(sub_thread);

    connect(my_thread,&MyThread::mySignal,this,&MyWidget::dealSignal_MyWidget);
    connect(my_thread,&MyThread::mySignal,my_thread,&MyThread::dealSignal_MyThread);

    qDebug()<<"主线程号:"<<QThread::currentThread();//Returns a pointer to a QThread which manages the currently executing thread.

    connect(this,&MyWidget::startThread,my_thread,&MyThread::myTimeout);//只能通过signal-slot方式启动子线程处理函数

    connect(this,&MyWidget::destroyed,this,&MyWidget::dealDestroyed);



}

MyWidget::~MyWidget()
{
    delete ui;
}
void MyWidget::dealSignal_MyWidget()
{
    static int i=0;
    i++;
    ui->lcdNumber->display(i);
    //qDebug()<<QThread::currentThread();

}

void MyWidget::on_ButtonStart_clicked()
{
    if(sub_thread->isRunning()==true)return;


    //启动子线程,但是并没有启动子线程处理函数
    sub_thread->start();

    my_thread->setFlag(false);
    //特别注意不能直接调用子线程处理函数
    //直接调用导致子线程处理函数和主线程是在同一个线程
    //不能这样写:my_thread->myTimeout();
    //只能通过signal-slot方式调用
    emit startThread();
}

void MyWidget::on_ButtonStop_clicked()
{
    if(sub_thread->isRunning()==false)return;
    my_thread->setFlag(true);
    sub_thread->quit();
    sub_thread->wait();
}
void MyWidget::dealDestroyed()
{
    this->on_ButtonStop_clicked();
    delete my_thread;//因为没有指定父对象,所以需要手动释放内存
}

程序中的逻辑关系如下图:
在这里插入图片描述
程序中几个小细节:
通常关闭子线程时会采用:

sub_thread->quit();
sub_thread->wait();

但是如果子线程是个死循环,那么wait是不会起作用的,所以解决办法是设置flag,在关闭子进程时跳出死循环。

面试题:connect()第5个参数的作用?
QMetaObject::Connection QObject::connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type = Qt::AutoConnection)
connect()第5个参数的作用是连接方式:默认,队列、直接
多线程时才有意义;
队列(Qt::QueuedConnection):槽函数所在的线程和信号接收者所在的线程一样;
直接(Qt::DirectConnection):槽函数所在的线程和信号发送者所在的线程一样;
默认(Qt::AutoConnection):如果是多线程,默认使用队列;如果是单线程,默认使用直接

所以,上面程序中:

connect(this,&MyWidget::startThread,my_thread,&MyThread::myTimeout);

等价于:

connect(this,&MyWidget::startThread,my_thread,&MyThread::myTimeout,Qt::QueuedConnection);
发布了241 篇原创文章 · 获赞 4 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/ShenHang_/article/details/105159903