1、Qt底层线程的使用方法(一)
1.1 QThead
创建和启动线程:
- 继承QThead,重写run方法
- 调用start开始执行
关闭线程:
- 使用标志位关闭线程
- 调用wait等待线程结束
- 强制关闭线程
mythread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QThread>
class MyThread : public QThread
{
Q_OBJECT
public:
MyThread(QObject* parent = nullptr);
void stop();
protected:
void run();
private:
bool is_stop = true;
};
#endif // MYTHREAD_H
mythread.cpp
#include "mythread.h"
#include <QDebug>
MyThread::MyThread(QObject *parent): QThread(parent)
{
}
void MyThread::stop()
{
is_stop = true;
bool ok = wait(2000);
if (ok)
{
qDebug() << QString::fromLocal8Bit("等待线程结束成功");
}
else
{
qDebug() << QString::fromLocal8Bit("等待线程结束失败");
this->terminate();
wait(1000);
}
}
void MyThread::run()
{
// 模拟数据库查询
is_stop = false;
qDebug() << QString::fromLocal8Bit("当前线程id ") << this->currentThreadId();
for (int i = 0; i < 5; i++)
{
qDebug() << QString::fromLocal8Bit("正在查询数据库") << i;
if (is_stop)
{
qDebug() << QString::fromLocal8Bit("终止查询");
return;
}
sleep(1);
}
qDebug() << QString::fromLocal8Bit("查询结束");
}
1.2 moveToThread
void QObject::moveToThread(QThread *targetThread)
使用方法:
- 定义一个继承于QObject的类,在这个类中可以定义多个槽函数,每个槽函数子线程执行的代码。
QThread* td = tdnew QThread
和worker类对象,使用moveToThread(td)
将worker中的事件循环交给ttd
处理。- 建立信号与槽函数的连接。
UserManager.h:定义两个槽函数,两个信号,声明QThread。
#ifndef USERMANAGER_H
#define USERMANAGER_H
#include <QObject>
#include <QThread>
class UserManager : public QObject
{
Q_OBJECT
public:
explicit UserManager();
~UserManager();
signals:
void searchBasicFinished(const QString& name);
void searchJobFinished(const QString& name);
public slots:
void searchBasic();
void searchJob();
private:
QThread* td;
};
#endif // USERMANAGER_H
UserManager.cpp
#include "usermanager.h"
#include <QDebug>
UserManager::UserManager()
: QObject{}
{
td = new QThread();
moveToThread(td);
td->start();
qDebug() << td->currentThreadId();
}
UserManager::~UserManager()
{
if (td->isRunning())
{
td->terminate();
td->wait(1000);
}
delete td;
td = nullptr;
}
void UserManager::searchBasic()
{
for (int i = 0; i < 3; i++)
{
qDebug() << QString::fromLocal8Bit("查询基本信息 ") << i << QThread::currentThreadId();
QThread::sleep(1);
}
emit searchBasicFinished(QString::fromLocal8Bit("张三"));
}
void UserManager::searchJob()
{
for (int i = 0; i < 3; i++)
{
qDebug() << QString::fromLocal8Bit("查询工作信息 ") << i << QThread::currentThreadId();
QThread::sleep(1);
}
emit searchJobFinished(QString::fromLocal8Bit("Qt工程师"));
}
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include "usermanager.h"
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
signals:
void searchBasic();
void searchJob();
public slots:
void onSearhBasicFinished(const QString& name);
void onSearhJobFinished(const QString &name);
private slots:
void on_btnSearchBasic_clicked();
void on_btnSearchJob_clicked();
private:
Ui::Widget *ui;
UserManager* manager;
};
#endif // WIDGET_H
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
manager = new UserManager();
connect(this, &Widget::searchBasic, manager, &UserManager::searchBasic);
connect(this, &Widget::searchJob, manager, &UserManager::searchJob);
connect(manager, &UserManager::searchBasicFinished, this, &Widget::onSearhBasicFinished);
connect(manager, &UserManager::searchJobFinished, this, &Widget::onSearhJobFinished);
}
Widget::~Widget()
{
delete ui;
delete manager;
}
void Widget::onSearhBasicFinished(const QString &name)
{
ui->textEditBasic->append(QString::fromLocal8Bit("查询基本信息,已接收:") + name);
}
void Widget::onSearhJobFinished(const QString &name)
{
ui->textEditJob->append(QString::fromLocal8Bit("查询工作信息,已接收:") + name);
}
void Widget::on_btnSearchBasic_clicked()
{
// 查询用户基本信息
emit searchBasic();
ui->textEditBasic->append(QString::fromLocal8Bit("查询基本信息,已发送"));
}
void Widget::on_btnSearchJob_clicked()
{
// 查询用户工作信息
emit searchJob();
ui->textEditJob->append(QString::fromLocal8Bit("查询工作信息,已发送"));
}
总结:
- 主线程发出信号,子线程对象的槽函数使用线程来执行
- 信号和槽是多对多关系
- 信号发出后立即返回,信号会放到队列中依次执行
- 槽函数执行完成后,使用信号进行通知
moveToThread比较QThread:
5. QThread只能执行一个run方法
6. 使用moveToThread,一个类可以执行多个方法
7. 如果不是长期运行的线程,推荐使用moveToThread
1.3 QThreadPool
QThreadPool是Qt框架提供的一个用于管理线程池的类,它可以帮助在应用程序中管理并发任务的执行。
- 创建QThreadPool对象:
QThreadPool *threadPool = QThreadPool::globalInstance(); // 获取全局线程池实例
- 创建自定义的任务类:
继承自QRunnable或者QObject,并实现run()方法,该方法中包含了任务的执行逻辑。
class MyTask : public QRunnable {
public:
void run() override {
// 任务执行逻辑
}
};
- 将任务提交给线程池:
MyTask *task = new MyTask();
threadPool->start(task); // 将任务提交给线程池
- 等待任务执行完成(可选):
threadPool->waitForDone(); // 等待所有任务执行完成
适用情况:
- 并发执行多个独立的任务:
如果你有多个任务需要并发执行,并且这些任务之间是相互独立的,那么可以使用QThreadPool来管理这些任务的执行。 - 避免频繁创建和销毁线程:
使用线程池可以避免频繁地创建和销毁线程,从而减少资源消耗和提高效率。 - 限制并发执行的任务数量:
可以通过设置线程池的最大线程数来限制并发执行的任务数量,以避免系统资源被过多地占用。 - 异步任务执行:
如果你需要在后台执行一些耗时的任务,但又不希望阻塞主线程,可以将这些任务提交给线程池来异步执行,从而保持界面的响应性。