Qt 多线程QThread

Qt 多线程(QThread)

一.理解QThread

关于QThread,参考qt助手中的解析

Detailed Description
A QThread object manages one thread of control within the program. QThreads begin executing in run(). By default, run() starts the event loop by calling exec() and runs a Qt event loop inside the thread.
You can use worker objects by moving them to the thread using QObject::moveToThread().
(详细说明
QThread对象管理程序中的一个控制线程。QThreads开始在run()中执行。默认情况下,run()通过调用exec()启动事件循环,并在线程内运行Qt事件循环。
您可以通过使用QObject::moveToThread()将辅助对象移动到线程来使用它们。)

注意:实际上,QThread可以理解为线程控制器,它本身并不是一个线程,只是QThread管理了一个线程而已。
实际上,继承自QThread的类中,下面两种情况的代码才是运行在次线程中。
(1).run()函数的代码。
(2).次线程执行exec()函数,开启事件循环。某个继承自QObject的类obj,通过moveToThread()函数移入次线程,
并且通过信号槽机制触发obj的槽函数(信号槽连接方式不能为 Qt::DirectConnection)

二.QThread的使用方法。

方法1新建一个类,继承类QThread,重载run()函数,并将关键代码放到run()中执行。执行完成后,发送信号,告诉主线程完成

//workerthread.h

#ifndef WORKERTHREAD_H
#define WORKERTHREAD_H
#include <QThread>

class WorkerThread : public QThread
{
    Q_OBJECT

public:
    WorkerThread(QObject *parent = nullptr);


protected:
    void run();

signals:
    void sigResult();
};

#endif // WORKERTHREAD_H

//workerthread.cpp

#include "workerthread.h"
#include <QDebug>

WorkerThread::WorkerThread(QObject *parent)
    :QThread(parent)
{

}

void WorkerThread::run()
{
    /*
     死循环,让线程一直跑。或者处理完毕就退出
     */
    qDebug()<<"WorkerThread run: threadId: "<<QThread::currentThreadId();
    emit sigResult();
}

//mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include "myThread1.h"

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private:
    Ui::MainWindow *ui;

private slots:
    void slotResult();
    void slotResultQueued();
    void slotResultDirect();

};
#endif // MAINWINDOW_H

//mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QThread>
#include <QDebug>
#include <workerthread.h>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    qDebug()<<"main threadId: "<<QThread::currentThreadId();
    WorkerThread *workthread = new WorkerThread(this);
    //匿名槽函数,槽函数运行在次线程
    connect(workthread, &WorkerThread::sigResult, [=]()
    {
        qDebug()<<"lanba slot threadId: "<<QThread::currentThreadId();
    });

    //普通连接方式,槽函数运行在主线程
    connect(workthread, &WorkerThread::sigResult, this, &MainWindow::slotResult);

    //队列连接
    connect(workthread, &WorkerThread::sigResult, this, &MainWindow::slotResultQueued, Qt::QueuedConnection);

    //直接连接
    connect(workthread, &WorkerThread::sigResult, this, &MainWindow::slotResultDirect, Qt::DirectConnection);
    workthread->start();
}

MainWindow::~MainWindow()
{
    delete ui;
}

//普通连接方式的槽函数
void MainWindow::slotResult()
{
    qDebug()<<"slotResult threadId: "<<QThread::currentThreadId();
}

//队列连接方式的槽函数
void MainWindow::slotResultQueued()
{
    qDebug()<<"slotResultQueued threadId: "<<QThread::currentThreadId();
}

//直接连接方式的槽函数
void MainWindow::slotResultDirect()
{
    qDebug()<<"slotResultDirect threadId: "<<QThread::currentThreadId();
}

运行结果:

在这里插入图片描述


结论:
使用上述方式调用线程槽函数所在的线程如下:
1.槽函数为匿名函数,槽函数运行在发送者所在的线程。
2.采用Qt::DirectConnection方式连接信号槽,槽函数运行在发送者所在的线程。
3.其它连接方式,槽函数运行在接受者所在的线程。

方法2:次线程在run()函数中执行exec()函数,开启事件循环。某个继承自QObject的类obj,通过moveToThread()函数移入次线程,
并且通过信号槽机制触发obj的槽函数(信号槽连接方式不能为 Qt::DirectConnection)

//workerThread.h

#ifndef WORKERTHREAD_H
#define WORKERTHREAD_H
#include <QThread>

class WorkerThread : public QThread
{
    Q_OBJECT

public:
    WorkerThread(QObject *parent = nullptr);


protected:
    void run();
};

//workerThread.cpp

#include "workerthread.h"
#include <QDebug>

WorkerThread::WorkerThread(QObject *parent)
    :QThread(parent)
{

}

void WorkerThread::run()
{
    qDebug()<<"WorkerThread run: threadId: "<<QThread::currentThreadId();
    exec();		//执行exec()函数,开启事件循环
}

//worker.h

#ifndef WORKER_H
#define WORKER_H

#include <QObject>

class Worker : public QObject
{
    Q_OBJECT

public:
    explicit Worker(QObject *parent = nullptr);


signals:
    void sigFinished();

public slots:
    void doSomething();
};

#endif // WORKER_H

//worker.cpp

#include "worker.h"
#include <QThread>
#include <QDebug>

Worker::Worker(QObject *parent) :
    QObject(parent)
{

}

void Worker::doSomething()
{
    qDebug()<<"worker doSomething: threadId: "<<QThread::currentThreadId();
    emit sigFinished();
}

//mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include "myThread1.h"

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private:
    Ui::MainWindow *ui;

signals:
    void sigDoSomething();

private slots:
    void slotFinished();
    void slotFinishedQueued();
    void slotFinishedDirect();

};
#endif // MAINWINDOW_H

//mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QThread>
#include <QDebug>
#include <workerthread.h>
#include <worker.h>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    qDebug()<<"main threadId: "<<QThread::currentThreadId();
    WorkerThread *workthread = new WorkerThread(this);
    Worker *worker = new Worker();      //不能设置父类
    worker->moveToThread(workthread);   //将worker 移动到 次线程

    //主线程发送信号,次线程接收信号,槽函数运行在接受者线程。
    connect(this, &MainWindow::sigDoSomething, worker, &Worker::doSomething);

    //匿名函数
    connect(worker, &Worker::sigFinished, [=]()
    {
        qDebug()<<"lanba slot threadId: "<<QThread::currentThreadId();
    });

    //普通连接方式,槽函数运行在接受者线程
    connect(worker, &Worker::sigFinished, this, &MainWindow::slotFinished);
    //队列连接,槽函数运行在接受者线程
    connect(worker, &Worker::sigFinished, this, &MainWindow::slotFinishedQueued, Qt::QueuedConnection);
    //直接连接,槽函数运行在发送者所在线程。
    connect(worker, &Worker::sigFinished, this, &MainWindow::slotFinishedDirect, Qt::DirectConnection);

    workthread->start();
    emit sigDoSomething();

}

MainWindow::~MainWindow()
{
    delete ui;
}

//普通连接方式的槽函数
void MainWindow::slotFinished()
{
    qDebug()<<"slotFinished threadId: "<<QThread::currentThreadId();
}

//队列连接方式的槽函数
void MainWindow::slotFinishedQueued()
{
    qDebug()<<"slotFinishedQueued threadId: "<<QThread::currentThreadId();
}

//直接连接方式的槽函数
void MainWindow::slotFinishedDirect()
{
    qDebug()<<"slotFinishedDirect threadId: "<<QThread::currentThreadId();
}

//运行 结果

在这里插入图片描述


结论,通过moveToThread()方式,确实可以将槽函数运行在次线程(信号槽连接方式不能为:Qt::DirectConnection)

相关文章

学习编程是顺着互联网的发展潮流,是一件好事。新手如何学习...
IT行业是什么工作做什么?IT行业的工作有:产品策划类、页面...
女生学Java好就业吗?女生适合学Java编程吗?目前有不少女生...
Can’t connect to local MySQL server through socket \'/v...
oracle基本命令 一、登录操作 1.管理员登录 # 管理员登录 ...
一、背景 因为项目中需要通北京网络,所以需要连vpn,但是服...