Qt5多线程编程的实现
为什么需要多线程
在程序运行时,为了保证其正常运行及良好的用户体验,需要避免阻塞UI线程。如果所有操作都在UI线程中执行,当需要执行比较耗时或无法预知执行时间的操作时(比如下载文件、读写磁盘等),程序会出现“卡住”的状况,导致用户无法继续进行操作,程序表现为假死状态,影响用户使用体验。
Qt5多线程编程实现
在Qt5中,多线程编程的实现主要由以下几个部分组成:
QThread
QThread是一个基础的多线程类,继承自QObject。在使用时,需要自定义一个继承自QThread的子类,并重写run()函数,该函数代表子线程的执行任务。创建子线程的一般方式为创建QThread的子类对象,并使用start()函数启动run()函数执行线程任务。
// 继承QThread实现子线程
class Thread : public QThread {
public:
void run() override {
// 子线程任务执行代码
}
};
// 创建并启动子线程
Thread* thread = new Thread();
thread->start();
QRunnable
QRunnable是一个轻量级的多线程类,继承自QObject,并实现了run()函数,当该类被传递给QThreadPool时,QThreadPool将自动创建一个线程并执行run()函数中的任务。创建QRunnable的一般方式是创建一个继承自QRunnable的子类,并实现run()函数。
// 继承自QRunnable实现轻量级的子线程任务
class Runnable : public QRunnable {
public:
void run() override {
// 子线程任务执行代码
}
};
// 创建线程池并添加任务
QThreadPool* pool = QThreadPool::globalInstance();
Runnable* runnable = new Runnable();
pool->start(runnable);
QtConcurrent
QtConcurrent是一个较为高级的多线程类,提供了一些现成的多线程操作符,比如map()、filter()等,可以极大地简化多线程操作。和QRunnable一样,QtConcurrent也是一个轻量级的多线程类,调用时只需要传入要执行的函数及参数即可。
// QtConcurrent操作,map操作将vector中的元素进行平方,并使用QFuture等待返回
QFuture <int> future = QtConcurrent::map(vector, [](int& element) {
return element * element;
});
future.waitForFinished();
QThreadPool
QThreadPool是多线程框架中的线程池,它可以管理线程的创建、关闭、等待及状态报告等。在使用时,需要先创建QThreadPool对象,并使用start()函数添加任务,QThreadPool会根据任务及系统性能自动管理线程数量。一旦线程池启用,只有在指定条件(如线程Idle时间、最大线程数等)下才停止工作。
// 创建线程池并添加任务
QThreadPool* pool = QThreadPool::globalInstance();
pool->setMaxThreadCount(4);
for (int i = 0; i < count; ++
Runnable* runnable = new Runnable();
pool->start(runnable);
}
pool->waitForDone();
示例说明
示例一:QThread实现
下面是一个使用QThread实现多线程的示例,包括主线程和子线程间的信号与槽传递。
// MyThread.h
class MyThread : public QThread {
Q_OBJECT
public:
MyThread(QObject* parent = nullptr);
signals:
void numberChanged(int number);
protected:
void run() override;
};
// MyThread.cpp
MyThread::MyThread(QObject* parent)
: QThread(parent){
}
void MyThread::run() {
int number = 0;
while(number < 100) {
emit numberChanged(number);
QThread::msleep(100);
++number;
}
}
// MainWindow.h
class MainWindow : public QMainWindow {
Q_OBJECT
public:
explicit MainWindow(QWidget* parent = nullptr);
public slots:
void numberReceived(int number);
private slots:
void on_pushButton_clicked();
private:
Ui::MainWindow* ui;
MyThread* m_thread;
};
// MainWindow.cpp
MainWindow::MainWindow(QWidget* parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow) {
ui->setupUi(this);
m_thread = new MyThread(this);
connect(m_thread, &MyThread::numberChanged, this, &MainWindow::numberReceived);
}
void MainWindow::numberReceived(int number) {
ui->lineEdit->setText(QString::number(number));
}
void MainWindow::on_pushButton_clicked() {
if(!m_thread->isRunning()) {
m_thread->start();
}
}
在该示例中,主线程界面中有一个push button和一个line edit,当用户点击push button时,将在子线程中启动任务,并每100ms向主线程信号槽传递数字。主线程接收到信号后,将数字显示到line edit控件中。
示例二:QtConcurrent实现
下面是一个使用QtConcurrent实现多线程的示例,实现了一个简单的对文件夹进行递归遍历并计算其所有文件大小的程序。
// DirSizeCalculator.h
class DirSizeCalculator {
public:
qint64 getDirSize(const QString& path);
private:
static qint64 countSize(const QString& path);
};
// DirSizeCalculator.cpp
qint64 DirSizeCalculator::getDirSize(const QString& path) {
QFuture <qint64> future = QtConcurrent::run(&countSize, path);
future.waitForFinished();
return future.result();
}
qint64 DirSizeCalculator::countSize(const QString& path) {
qint64 size = 0;
QFileInfo file_info(path);
if(file_info.isFile()) {
size += file_info.size();
} else if(file_info.isDir()) {
QDir dir(path);
QStringList names = dir.entryList(QDir::AllEntries | QDir::NoDotAndDotDot);
foreach(QString name, names) {
QString full_path = QDir(path).filePath(name);
if(QFileInfo(full_path).isFile()) {
size += QFileInfo(full_path).size();
} else if(QFileInfo(full_path).isDir()) {
size += countSize(full_path);
}
}
}
return size;
}
// MainWindow.h
class MainWindow : public QMainWindow {
Q_OBJECT
public:
explicit MainWindow(QWidget* parent = nullptr);
private slots:
void on_pushButton_clicked();
private:
Ui::MainWindow* ui;
DirSizeCalculator* m_calculator;
};
// MainWindow.cpp
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow) {
ui->setupUi(this);
m_calculator = new DirSizeCalculator();
}
void MainWindow::on_pushButton_clicked() {
QString folder_path = QFileDialog::getExistingDirectory(this, "Please select folder");
qint64 size = m_calculator->getDirSize(folder_path);
QString display_text = QString("Folder size: %1 Bytes").arg(size);
ui->label->setText(display_text);
}
在该示例中,主线程界面中有一个push button和一个label,当用户点击push button时,将弹出文件夹选择对话框,当用户选择文件夹后,程序将启动QtConcurrent的线程执行DirSizeCalculator中的 getDirSize() 函数,并等待线程返回结果,线程会利用递归方式计算文件夹下所有文件的大小,并将结果返回给主线程进行显示。
总结
多线程编程的实现在Qt5中非常便捷,开发者可以根据实际需求选择合适的多线程类,避免UI线程阻塞,降低程序出现假死等异常情况的概率,提高程序可靠性。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Qt5多线程编程的实现 - Python技术站