前言
Qt实现在线程中执行任务有4种方法,分别是:
1. 创建一个派生于 QThread 类的子类,重写run函数,在run函数中执行任务
2. 创建一个派生于 QObject 的子类,调用 QObject::moveToThread() 方法将子类对象移动到子线程对象中。
3. 使用线程池QThreadPool
4. 使用QtConcurrent执行并行任务
这4种方法的完整代码示例在绑定的资源中,大家可以免费下载。
一 重写run函数的方法执行任务
局限性:只能在 run 函数中处理任务
使用步骤:
1. 创建一个派生于 QThread 类的子类。2. 在子类中重写 run 函数,并在 run 函数中编写子线程执行的任务代码。
注意:不能在类的外部调用 run() 方法启动子线程,只能通过调用 start() 方法启动子线程,启动子线程后,子线程会自动执行 run 任务。3. 在主线程中创建子线程对象,new 一个就行。
4. 在主线程中关联 QThread::finished 信号,用于在槽函数中清理子线程资源,释放内存。
5. 在主线程中关联子线程中的自定义信号,用于主线程和子线程之间的通信。
6. 调用 start() 方法启动子线程,执行 run 中的任务。
7. 子线程执行完毕释放内存,清理资源。
代码示例:
1. 子线程类头文件:
protected:
// 重写 QThread 类的虚函数,在该函数中处理子线程任务
void run();
signals:
// 自定义信号, 用于和主线程通信
void curNumber(int num);
2. 子线程类源文件:
// 重写 QThread 类的虚函数,在该函数中处理子线程任务
void SubThread::run()
{
// QThread::currentThread():获取当前线程的 QThread 对象指针
qDebug() << "当前线程对象的地址: " << QThread::currentThread();
int num = 0;
while(1)
{
// 发送自定义信号,向主线程中传递数据
emit curNumber(num++);
if(num == 10000000)
{
break;
}
//阻塞,让当前子线程暂停1秒
QThread::sleep(1);
}
qDebug() << "run() 执行完毕, 子线程退出...";
}
3. 主线程的构造函数中:
// QThread::currentThread():获取当前线程的 QThread 对象指针
qDebug() << "主线程对象地址: " << QThread::currentThread();
// 创建子线程对象
SubThread* subThread = new SubThread;
// 将子线程的自定义信号和主线程中的 Lambda 表达式表示的槽函数关联,用于传递数据
connect(subThread, &SubThread::curNumber, this, [=](int num)
{
// 在 label 控件中显示数字
ui->label->setNum(num);
});
// 点击 startBtn 按钮,启动子线程
connect(ui->pushButton, &QPushButton::clicked, this, [=]()
{
// 启动子线程,子线程自动执行 run 函数中的任务
subThread->start();
});
//将子线程的信号和主线程的 Lambda 表达式表示的槽函数关联,用于在线程任务执行完成后清理子线程资源,释放内存
// connect(subThread, &SubThread::finished, this, [&](){
// //释放子线程对象的堆内存
// delete subThread;
// });
//在子线程对象 subThread 的任务执行结束后,在 subThread 的事件循环结束后释放子线程对象 subThread 的内存
//注意:subThread 线程对象的 run 函数中需要启用事件循环(exec())。
connect(subThread, &QThread::finished, subThread, &QObject::deleteLater);
二 移动子类对象到子线程对象
相比重写Run函数的优势:可以定义多个成员函数处理任务
使用步骤:
1. 创建一个派生于 QObject 的子类,在子类中创建自定义的公共成员函数(建议创建为槽函数,方便与子线程启动信号 QThread::started() 关联),用于执行子线程的任务(相当于 run 函数)。
2. 在主线程中实例化子线程对象(QThread) 和实例化上一步创建的子类对象。
注意:千万不要给子类对象指定父对象3. 调用 QObject::moveToThread() 方法将子类对象移动到子线程对象中。
4. 在主线程中将子线程对象的 QThread::started() 信号和子类对象中执行任务的槽函数关联,这样,在启动子线程时会自动执行任务
5. 在主线程中关联子线程中的自定义信号,用于主线程和子线程之间的通信。
6. 在主线程中关联 QThread::finished 信号,用于在槽函数中清理子线程和子对象资源,释放内存。
7. 调用 start() 函数启动子线程。
注意:子线程启动后,移动到子线程中的子类对象并没有立即执行任务(自定义的公共成员槽函数)8. 由于将子线程对象的 QThread::started() 信号和子类对象中执行任务的槽函数关联了,子线程启动后,子类对象中的任务也就自动执行了。
9. 子线程任务执行完毕释放子线程对象和子类对象内存,清理资源。
代码示例:
1. 子类对象头文件:
public slots:
//创建自定义公共成员函数(建议创建为槽函数,方便与子线程启动信号 QThread::started() 关联),用于执行子线程任务
void working();
signals:
// 自定义信号, 用于和主线程通信
void curNumber(int num);
2. 子类对象源文件:
// 创建自定义公共成员函数(建议创建为槽函数,方便与子线程启动信号 QThread::started() 关联),用于执行子线程任务
void MyWork::working() {
// QThread::currentThread():获取当前线程的 QThread 对象指针
qDebug() << "当前线程对象的地址: " << QThread::currentThread();
int num = 0;
while (1) {
// 发送自定义信号,向主线程中传递数据
emit curNumber(num++);
if (num == 10000000) {
break;
}
// 阻塞,让当前子线程暂停1秒
QThread::sleep(1);
}
qDebug() << "run() 执行完毕, 子线程退出...";
}
3. 主线程的构造函数:
// QThread::currentThread():获取当前线程的 QThread 对象指针
qDebug() << "主线程对象地址: " << QThread::currentThread();
// 创建线程对象
QThread* sub = new QThread;
// 创建工作的类对象
// 注意:千万不要指定给创建的对象指定父对象
MyWork* work = new MyWork;
// 将工作的类对象移动到创建的子线程对象中
work->moveToThread(sub);
//将子线程对象的 QThread::started() 信号和子类对象中执行任务的槽函数关联,这样,在启动子线程时会自动执行任务
connect(sub, &QThread::started, work, &MyWork::working);
// 将子线程的自定义信号和主线程中的 Lambda 表达式表示的槽函数关联,用于传递数据
connect(work, &MyWork::curNumber, this, [=](int num)
{
// 在 label 控件中显示数字
ui->label->setNum(num);
});
// 点击 startBtn 按钮,执行子线程任务
connect(ui->pushButton, &QPushButton::clicked, this, [=]()
{
// 启动线程
sub->start();
});
//将子线程的 QThread::finished 信号和主线程的 Lambda 表达式表示的槽函数关联,用于在线程任务执行完成后清理子线程资源,释放内存
// connect(sub, &QThread::finished, this, [&](){
// //释放子线程对象的堆内存
// delete sub;
// //释放工作类对象
// delete work;
// });
//在子线程对象 sub 的任务执行结束后,在子线程对象 sub 和子类对象 word 的事件循环结束后释放子线程对象 sub 和子类 work 的内存
注意:sub 的 run 函数中需要启用事件循环(exec())
connect(sub, &QThread::finished, sub, &QObject::deleteLater);
connect(sub, &QThread::finished, work, &QObject::deleteLater);
三 使用线程池QThreadPool
QThreadPool说明:
1. Qt 中的 QThreadPool 类管理了一组线程, 里边还维护了一个任务队列。
2. 每个 Qt 应用程序都有一个全局 QThreadPool 对象,可以通过调用 globalInstance() 来访问它。也可以单独创建一个 QThreadPool 对象使用。
3. 线程池中的线程的任务执行完毕,该线程不会立即结束,而是由线程池自动管理该线程是挂起、继续执行任务还是释放等。
常用成员函数:
1. 获取 Qt 应用程序的全局线程池对象指针
static QThreadPool * QThreadPool::globalInstance();
2. 线程池中的线程个数2.1 获取线程池中的最大活动线程个数
int maxThreadCount() const;2.2 设置线程池中的最大活动线程个数
//说明:无论提交到该线程池的任务数量有多少,同时执行的任务将不会超过设置的最大活动线程数。
void setMaxThreadCount(int maxThreadCount);2.3 获取线程池中正在工作的线程个数
int QThreadPool::activeThreadCount() const;
3. 向线程池中添加任务(QRunnable 类型)3.1 如果线程池中没有空闲的线程了, 任务会放到任务队列中, 等待线程处理
//参数1:任务对象指针
//参数2:指定任务的执行优先级。默认值为0,表示正常优先级。数值越大,优先级越高。
void QThreadPool::start(QRunnable * runnable, int priority = 0);3.2 如果线程池中没有空闲的线程了, 直接返回, 任务添加失败, 任务不会添加到任务队列中
bool QThreadPool::tryStart(QRunnable * runnable);
4. 删除线程池中的任务4.1 尝试将某一个任务从线程池的任务队列中删除, 如果任务已经开始执行就无法删除了
bool QThreadPool::tryTake(QRunnable *runnable);4.2 将线程池中的任务队列中所有未开始处理的任务删除, 如果任务已经开始执行就无法删除了
void QThreadPool::clear();
使用步骤:
1. 创建派生于 QRunnable 的任务子类。
1.1 重写 run 函数,在 run 函数中编写任务的执行代码。
1.2 在任务子类的构造函数中设置任务对象在执行完毕后自动销毁。2. 如果想在任务中使用 Qt 的信号槽机制进行数据传递,那任务子类除了继承 QRunnable 类外,还需要继承 QObject 类。
3. 获取 Qt 应用程序的全局线程池对象指针。
4. 设置线程池中的最大活动线程个数。
5. 创建任务对象,并将其添加到线程池中。
四 使用QtConcurrent执行并行任务
说明:这个模块极大的简化了Qt线程的使用,我们只需要调用它的API,在应用程序的全局线程池对象中安排执行某个函数而无需关心线程的创建管理和释放
代码示例:
举例1:在线程池中的线程中执行的无返回值无参数函数
void Fun1()
{
qDebug() << "使用 QtConcurrent::run 执行无返回值无参数函数测试...";
}
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 使用 lambda 表达式在 QtConcurrent::run() 中调用要执行的 Fun1() 函数
QFuture<void> future1 = QtConcurrent::run([](){
Fun1();
});
// 等待线程池中的 Fun1() 函数执行完毕
future1.waitForFinished();
}
举例2:在线程池中的线程中执行的有返回值有参数函数
int Fun2(int number) {
return number * number;
}
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 使用 lambda 表达式包装有返回值的函数(传递参数5)
QFuture<int> future2 = QtConcurrent::run([](int num){
return Fun2(num);
}, 5);
// 等待线程池中的 doSomethingUseful() 函数执行完毕
future2.waitForFinished();
// 获取函数的返回值
int result = future2.result();
qDebug() << "The square of 5 is:" << result;
}
标签:Qt,对象,子类,任务,线程,QThread,函数
From: https://blog.csdn.net/qq_36318563/article/details/140364426