Qt:QThread

0、说明

QThread提供了一种与平台无关的线程管理方法。

一个QThread对象管理一个线程。QThread通过run()方法启动线程。默认情况下,run()方法通过exec()启动一个事件循环,并且在线程中运行这个时间循环。

我们可以通过调用QObject::moveToThread()来把某项事务转移到Thread中,下面以一个Worker类为例:

复制代码
class Worker : public QObject
{
    Q_OBJECT
public slots:
    void doWork(QString parameter){
        QString result;
        /*... 在此处填写比较重要、需要阻塞的操作 ...*/
        emit resultReady(result);
    }
    
signals:
    void resultReady(QString result);
    
};

class Controller : public QObject
{
    Q_OBJECT
    QThread workerThread;
public:
    Controller(){
        Worker * worker = new Worker;
        worker->moveToThread(&workerThread);
        connect(&workerThread,&QThread::finished,worker,&QObject::deleteLater);
        connect(this,&Controller::operate,worker,&Worker::doWork);
        connect(worker,&Worker::resultReady,this,&Controller::handleResults);
        workerThread.start()
    }
    ~Controller(){
        workerThread.quit();
        workerThread.wair();
    }
public slots:
    void handleResults(QString);
signals:
    void operate(QString);
};
复制代码

Worker类的槽函数中的代码将在另一个Thread中执行。不过,我们可以自由地在任何线程中,连接Worker任何对象的任何信号中。在不同的线程中连接信号与槽是安全的,因为才采用了队列连接的机制。

另一种使代码运行在不同的线程中的方法是,构造QThread子类覆写run()方法,例如:

复制代码
class WorkerThread : public QThread
{
    Q_OBJECT
    void run() override {
        QString result;
        /*...在这里写一些代价高昂且需要阻塞的操作...*/
        emit resultReady(result);
    }
signals:
    void resultReady(QString);
};

void MyObject :: startWorkInAThread()
{
    WorkerThread * workerThread = new WorkerThread(this);
    connect(workerThread,&WorkerThread::resultReady,this,&MyObject::handleResults);
    connect(workerThread,&WorkerThread::finished,workerThread,&QObject::deleteLater);
    workerThread->start();
}
复制代码

在本例中,线程在run()函数结束后就会退出。除非调用exec(),否则在该线程中就不会存在任何事件循环。

一个QThread对象是生存在初始化它的老线程中的,并不是存在于调用run()新线程中,这意味着QThread的全部队列槽调用方法都会在老线程中执行。因此,开发者如果想在新线程中调用槽函数,必须使用worker-object方法,这个槽函数不能直接在该QThread子类中实现。

不同于队列槽调用的方法,直接在QThread中执行的方法将会在该线程中运行。当实现一个QThread子类时,在新线程中调用run()方法,但是是在老线程中构造该新线程。如果一个成员变量被多个函数调用,那么实际上该变量是被多个线程访问的,这一过程是安全的。

注意

不同线程间的Object进行交流时,要特别小心。通常情况下,函数调用应该在创建QThread的线程中进行(例如setPriority()),除非有文件说明有其他用法。

 线程管理

QThread在started()finished()时会发送一个信号,此外如果想检查的话,也可以通过isFinished()、isRunning()来检查线程的状态。

可以通过exit()、quit()来终止线程。有时,我们会想用terminate()来终止一个线程,但是这样做是危险且不推荐的。可以查看terminate()、setTerminationEnabled()来查看详细说明。

从Qt 4.8之后,可以在进程结束时,通过将finished()信号与QObject::deleteLater()连接,以释放资源。

调用wait()阻塞一个线程,直到另一个线程finish,或者直到指定时间耗尽。

QThread也提供了一些static、平台无关休眠函数sleep()、msleep()、usleep(),这三个函数的功能类似,只是单位不同,分别是秒、毫秒、微秒

注意

wait()sleep()通常是不必要的,因为Qt是事件驱动的框架。不推荐调用wait(),可以用监听finished()信号并将它与另一个槽函数连接来代替;不推荐调用sleep(),推荐使用QTimer

static方法currentThreadId()currentThread()返回当前执行的线程,前一个函数返回线程ID,后一个返回线程指针。

为了选择线程名(Linux中通过ps -L命令来指定),我们可以在启动线程前调用setObjectName()。不调用该方法的话,线程名将是运行该线程的线程子类名。不过该名字在Windows系统中用release方式来编译代码时是不可用的。

1、模块和加载项

Header: #include <QThread>
qmake: QT += core
Inherits: QObject

2、构造与析构

QThread(QObject *parent = nullptr)

构造一个QThread来管理一个新线程。

parent是QThread的所有者。

该线程直至调用start()才会启动

~QThread()

破坏QThread。

注意:删除QThread将会stop线程的执行。删除一个运行中QThread将会导致程序碰撞。删除QThread前应该先等待finished()信号。

 

3、静态字段

enum Priority

{ IdlePriority, LowestPriority, LowPriority,

NormalPriority, HighPriority, …, InheritPriority }

线程优先级

ConstantValueDescription
QThread::IdlePriority 0 scheduled only when no other threads are running.
QThread::LowestPriority 1 scheduled less often than LowPriority.
QThread::LowPriority 2 scheduled less often than NormalPriority.
QThread::NormalPriority 3 the default priority of the operating system.
QThread::HighPriority 4 scheduled more often than NormalPriority.
QThread::HighestPriority 5 scheduled more often than HighPriority.
QThread::TimeCriticalPriority 6 scheduled as often as possible.
QThread::InheritPriority 7 use the same priority as the creating thread. This is the default.

4、静态方法

返回值类型

方法

说明

QThread *

create(Function &&f, Args &&... args)

create(Function &&f)

构造一个新的QThread,该线程将执行函数f,后边的参数是函数f的参数。

该线程并没有启动——必须直接通过start()才能启动。这允许我们连接它们的信号、把某些QObject转移到Thread中、确定优先级等。

支持参数的create方法只有在使用C++17时才能用。

不要多次在QThread中调用start()

QThread * currentThread() 返回当前执行的线程
Qt::HANDLE currentThreadId() 返回当前执行的线程的Handle
int idealThreadCount() 返回系统中能运行的线程的最大数量。
void sleep(unsigned long secs) 休眠若干秒
void msleep(unsigned long msecs) 休眠若干毫秒
void usleep(unsigned long usecs) 休眠若干微秒
void yieldCurrentThread()  

 

5、实例方法

返回值类型

方法

说明

QAbstractEventDispatcher * eventDispatcher() 指向该线程的Event Dispatcher。如果不存在时则返回nullptr。
protected int exec()

进入事件循环,直至调用exit(),返回传递给exit()的值。如果通过quit()调用exit()则返回0。

该方法内部将会执行run(),调用该方法来启动一个消息循环。

该方法只能被当前线程调用。

void exit(int returnCode = 0)

使线程的事件循环终止并返回一个returnCode。

0代表成功,非0代表失败。

bool

isFinished()

isInterruptionRequested()

isRunning()

如果线程完成时,返回true
如果在该线程中运行的任务需要被终止,返回true
当线程正在运行时,返回true
int loopLevel() 返回当前线程的事件循环等级。
QThread::Priority priority() 返回当前线程优先级。
void requestInterruption() 请求线程的中止。
void

setEventDispatcher(QAbstractEventDispatcher *eventDispatcher)

setPriority(QThread::Priority priority)

setStackSize(uint stackSize)

设置EventDispatched、优先级、栈大小。
uint stackSize() 返回当前线程的栈大小
bool

wait(QDeadlineTimer deadline = QDeadlineTimer(QDeadlineTimer::Forever))

 

 

wait(unsigned long time)

1、阻塞当前线程直到以下情况发生:

①该QThread相关的Thread完成执行;此时返回true

②deadline时间到了;此时返回false

2、等待若干秒

     

6、槽

quit()

使线程的事件循环退出,返回0(成功时)。等同于调用QThread::exit(0)

如果不存在消息循环,该方法将什么都不做。

start(QThread::Priority priority = InheritPriority) 通过调用run()来运行该线程。OS将根据参数priority来调度线程。如果线程已经在运行了,那么该方法讲什么都不做。
terminate() 终止线程运行,不推荐。

7、信号

finished()

线程正要完成执行前发送该信号。

发送该信号之后,事件循环就停止了,不会有更多事件在线程中运行了,除非一些推迟的资源回收、删除事件。

该事件常与QObject::deleteLater()连接,用于释放线程中的资源。

注意:如果通过terminate()来终止进程,那么就不能确定该信号是被哪个线程发送了。

started() 当线程开始执行时发送该信号,在run()方法调用前。
posted @   ShineLe  阅读(582)  评论(0编辑  收藏  举报
编辑推荐:
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
阅读排行:
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
点击右上角即可分享
微信分享提示