8 线程
约 2466 字大约 8 分钟
2025-06-22
头文件<QThread> 属于core模块 父类是QObject
QThread
常用成员函数
判断
判断线程中的任务是否处理完毕 完毕返回true,未完毕返回false
bool QThread::isFinished() const;
判断子线程是否在执行任务 正在执行返回ture,没有执行(执行完毕)返回false
bool QThread::isRunning() const;
获取属性
获取当前线程优先级
Priority QThread::priority() const;
设置属性
设置线程优先级 优先级是一个枚举值从低到高排列:
- QThread::IdlePriority --> 最低的优先级
- QThread::LowestPriority
- QThread::LowPriority
- QThread::NormalPriority
- QThread::HighPriority
- QThread::HighestPriority
- QThread::TimeCriticalPriority
- QThread::InheritPriority -->最高的优先级 子线程和其父线程的优先级相同, 默认是这个
void QThread::setPriority(Priority priority);
执行行为
退出 退出线程, 停止底层的事件循环
退出线程的工作函数
void QThread::exit(int returnCode = 0);
调用线程退出函数之后, 当前任务有可能还没有完成,需要等待线程执行完成后退出 一般调用了之后后边跟着调用wait函数等待线程执行完毕再退出
等待 等待任务完成, 然后退出线程, 一般情况下会在 exit() 后边调用这个函数
bool QThread::wait(unsigned long time = ULONG_MAX);
任务处理函数 子线程要处理什么任务, 需要写到 run() 中
[virtual protected] void QThread::run();
这个run()
是一个虚函数,如果想让创建的子线程执行某个任务,需要写一个子类让其继承QThread
,并且在子类中重写父类的run()
方法,函数体就是对应的任务处理流程。另外,这个函数是一个受保护的成员函数,不能够在类的外部调用,如果想要让线程执行这个函数中的业务流程,需要通过当前线程对象调用槽函数start()
启动子线程,当子线程被启动,这个run()
函数也就在线程内部被调用了。
构造函数
需要传递一个父对象,父对象和父类是不同的. 父类是按照继承关系来说的,父对象是按照从属关系来说的 在创建对象的时候qt维护了一个对象树, 对象树是用来进行内存回收的 像一个窗口中有许多控件,这些控件的父类不一定是这个窗口类,但目前这个窗口就是它们的父对象
QThread::QThread(QObject *parent = Q_NULLPTR);
信号函数
线程执行完成 线程中执行的任务完成了, 发出该信号
任务函数中的处理逻辑执行完毕了
[signal] void QThread::finished();
线程开始工作 开始工作之前发出这个信号, 一般不使用
[signal] void QThread::started();
槽函数
线程退出 和调用 exit() 效果是一样的
调用这个函数之后, 也需要调用 wait() 函数等待线程执行完毕
[slot] void QThread::quit();
启动子线程
[slot] void QThread::start(Priority priority = InheritPriority);
线程退出(强制) 线程退出, 可能是会马上终止线程, 一般情况下不使用这个函数
[slot] void QThread::terminate();
静态函数
返回一个指向管理当前执行线程的QThread的指针
[static] QThread *QThread::currentThread();
返回可以在系统上运行的理想线程数 和当前电脑的 CPU 核心数相同
[static] int QThread::idealThreadCount();
线程休眠函数
[static] void QThread::msleep(unsigned long msecs); // 单位: 毫秒
[static] void QThread::sleep(unsigned long secs); // 单位: 秒
[static] void QThread::usleep(unsigned long usecs); // 单位: 微秒
使用方法
方法1
- 需要创建一个线程类的子类,让其继承QT中的线程类 QThread,比如:
class MyThread:public QThread{ ...... }
- 重写父类的 run() 方法,在该函数内部编写子线程要处理的具体的业务流程
class MyThread:public QThread{
protected:
void run(){ ... }
}
- 在主线程中创建子线程对象,new 一个就可以了
MyThread * subThread = new MyThread;
- 启动子线程, 调用 start() 方法
subThread->start();
不能在类的外部调用run() 方法启动子线程,在外部调用start()相当于让run()开始运行 当子线程别创建出来之后,父子线程之间的通信可以通过信号槽的方式在Qt中在子线程不允许操作程序中的窗口类型对象 只有主线程才能操作程序中的窗口对象, 默认的线程就是主线程, 自己创建的就是子线程
这种在程序中添加子线程的方式是非常简单的,但是也有弊端,假设要在一个子线程中处理多个任务,所有的处理逻辑都需要写到run()函数中,这样该函数中的处理逻辑就会变得非常混乱,不太容易维护
。
方法2
用起来更加灵活,但是这种方式写起来会相对复杂一些
- 创建一个新的类,让这个类从QObject派生
class MyWork:public QObject{ ....... }
- 在这个类中添加一个公共的成员函数,函数体就是我们要子线程中执行的业务逻辑
class MyWork:public QObject{
public:
void working();// 函数名自己指定, 叫什么都可以, 参数可以根据实际需求添加
}
- 在主线程中创建一个QThread对象, 这就是子线程的对象 这时用于运行工作函数的子线程对象,需要把要执行任务的QObject子类移动到此线程中
QThread* sub = new QThread;
- 在主线程中创建工作的类对象(千万不要指定给创建的对象指定父对象)
MyWork* work = new MyWork(this); // error
MyWork* work = new MyWork; // ok
- 将MyWork对象移动到创建的子线程对象中, 需要调用QObject类提供的
moveToThread()
方法 移动到指定的子线程中工作
- targetThread: 要移动的目标子线程
void QObject::moveToThread(QThread *targetThread);
- 启动子线程,调用
start()
, 这时候线程启动了, 但是移动到线程中的对象并没有工作 - 调用MyWork类对象的工作函数,让这个函数开始执行,这时候是在移动到的那个子线程中运行的
线程池
QRunnable
在Qt中使用线程池需要先创建任务,添加到线程池中的每一个任务都需要是一个QRunnable
类型,因此在程序中需要创建子类继承QRunnable
这个类,然后重写 run()
方法,在这个函数中编写要在线程池中执行的任务,并将这个子类对象传递给线程池,这样任务就可以被线程池中的某个工作的线程处理掉了。
继承自它的子类是一个多重继承,如果需要在这个任务中使用Qt的信号槽机制进行数据的传递就必须继承QObject
这个类,如果不使用信号槽传递数据就可以不继承了,只继承QRunnable
即可。
成员函数
在子类中必须要重写的函数, 里边是任务的处理流程
[pure virtual] void QRunnable::run();
- autoDelete: 是否自动销毁 true: 这个任务对象在线程池中的线程中处理完毕,后会自动销毁
false: 这个任务对象在线程池中的线程中处理完毕后需要手动销毁
void QRunnable::setAutoDelete(bool autoDelete);
获取当前任务对象的析构方式,返回true->自动析构, 返回false->手动析构
bool QRunnable::autoDelete() const;
QThreadPool
QThreadPool
类管理了一组 QThreads
, 里边还维护了一个任务队列。QThreadPool
管理和回收各个 QThread
对象,以帮助减少使用线程的程序中的线程创建成本。每个Qt应用程序都有一个全局 QThreadPool
对象,可以通过调用 globalInstance()
来访问它。也可以单独创建一个 `QThreadPool 对象使用。
常用函数
获取和设置线程中的最大线程个数
int maxThreadCount() const;
void setMaxThreadCount(int maxThreadCount);
给线程池添加任务 任务是一个 QRunnable 类型的对象
如果线程池中没有空闲的线程了, 任务会放到任务队列中, 等待线程处理
void QThreadPool::start(QRunnable * runnable, int priority = 0);
判断是否有空闲的线程 如果线程池中没有空闲的线程了, 直接返回值, 任务添加失败, 任务不会添加到任务队列中
bool QThreadPool::tryStart(QRunnable * runnable);
查询正在工作线程的个数 线程池中被激活的线程的个数(正在工作的线程个数)
int QThreadPool::activeThreadCount() const;
删除任务 尝试性的将某一个任务从线程池的任务队列中删除, 如果任务已经开始执行就无法删除了
bool QThreadPool::tryTake(QRunnable *runnable);
将线程池中的任务队列里边没有开始处理的所有任务删除, 如果已经开始处理了就无法通过该函数删除了
void QThreadPool::clear();
获取线程池对象 在每个Qt应用程序中都有一个全局的线程池对象, 通过这个函数直接访问这个对象
static QThreadPool * QThreadPool::globalInstance();
一般情况下,我们不需要在Qt程序中创建线程池对象,直接使用Qt为每个应用程序提供的线程池全局对象即可。得到线程池对象之后,调用start()
方法就可以将一个任务添加到线程池中,这个任务就可以被线程池内部的线程池处理掉了,使用线程池比自己创建线程的这种多种多线程方式更加简单和易于维护。
贡献者
版权所有
版权归属:PinkDopeyBug