使用线程对象QThread
以下的代码仅列出紧要的部分。
QThread是一个线程管理类
cpp
//mthread.h
class MThread : public QThread
{
Q_OBJECT
public:
MThread();
// QThread interface
void printA()
{
qDebug()<<"A";
}
// QThread interface
protected:
void run() override;
};
void MThread::run()
{
qDebug()<<"Thread:"<<QThread::currentThreadId();
}
//main.cpp
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MThread* mt=new MThread;
MainWindow w;
QObject::connect(&w,&MainWindow::prepared,mt,&MThread::printA);
w.show();
qDebug()<<QThread::currentThreadId();//主线程ID
//这里没有调用mt->start();线程的任务函数MThread::run()没有执行
emit w.prepared();
return a.exec();
}
使用一个继承自QObject的Worker类来完成耗时的任务
cpp
void MThread::run()
{
qDebug()<<"Thread:"<<QThread::currentThreadId();
}
//worker.cpp
Worker::Worker(QObject *parent) : QObject(parent)
{
qDebug()<<"create worker at thread:"<<QThread::currentThreadId();
}
void Worker::printW()
{
for(int i=0;i<10;i++)
{
QThread::sleep(1);
qDebug()<<"."<<i<<"at thread:"<<QThread::currentThreadId();
}
}
//main.cpp
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
//当使用QThread时,Worker的任务是可以执行起来的,但是使用MThread时,线程任务是没有执行起来的--原因在于MThread的线程函数run()内没有开启事件循环
MThread* mt=new MThread;
// QThread* mt=new QThread;
MainWindow w;
w.show();
//一种推荐的多线程使用范式,继承自QObject的Worker类,将其移入子线程中去,它的槽函数将在子线程中执行
Worker* wk=new Worker();
wk->moveToThread(mt);
QObject::connect(&w,&MainWindow::prepared,wk,&Worker::printW);
mt->start();
qDebug()<<QThread::currentThreadId();//主线程ID
emit w.prepared();
return a.exec();
}
任务结束时,通过一个指定的信号来告诉主线程
cpp
//mthread.cpp
MThread::MThread()
{
}
void MThread::run()
{
qDebug()<<"Thread:"<<QThread::currentThreadId();
this->exec();
}
//worker.cpp
Worker::Worker(QObject *parent) : QObject(parent)
{
qDebug()<<"create worker at thread:"<<QThread::currentThreadId();
}
void Worker::printW()
{
for(int i=0;i<10;i++)
{
QThread::sleep(1);
qDebug()<<"."<<i<<"at thread:"<<QThread::currentThreadId();
}
emit taskDone();
}
//main.cpp
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MThread* mt=new MThread;
// QThread* mt=new QThread;
MainWindow w;
w.show();
//一种推荐的多线程使用范式,继承自QObject的Worker类,将其移入子线程中去,它的槽函数将在子线程中执行
Worker* wk=new Worker();
wk->moveToThread(mt);
QObject::connect(&w,&MainWindow::prepared,wk,&Worker::printW);
//taskDone信号是在子线程中发出的
QObject::connect(wk,&Worker::taskDone,&w,&MainWindow::hide);//当任务完成后,将窗体隐藏
mt->start();
qDebug()<<QThread::currentThreadId();//主线程ID
emit w.prepared();
return a.exec();
}
跨线程的信号槽,默认使用了队列连接,所以任务printW是在进入了事件循环才开始执行的
cpp
//mthread.cpp
MThread::MThread()
{
}
void MThread::run()
{
qDebug()<<"Thread:"<<QThread::currentThreadId();
this->exec();
}
//worker.cpp
Worker::Worker(QObject *parent) : QObject(parent)
{
qDebug()<<"create worker at thread:"<<QThread::currentThreadId();
}
void Worker::printW()
{
for(int i=0;i<10;i++)
{
qDebug()<<"."<<i<<"at thread:"<<QThread::currentThreadId();
QThread::sleep(1);
}
emit taskDone();
}
//main.cpp
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
//当使用QThread时,Worker的任务是可以执行起来的,但是使用MThread时,线程任务是没有执行起来的
MThread* mt=new MThread;
// QThread* mt=new QThread;
MainWindow w;
w.show();
//一种推荐的多线程使用范式,继承自QObject的Worker类,将其移入子线程中去,它的槽函数将在子线程中执行
Worker* wk=new Worker();
wk->moveToThread(mt);
QObject::connect(&w,&MainWindow::prepared,wk,&Worker::printW);
//taskDone信号是在子线程中发出的
QObject::connect(wk,&Worker::taskDone,&w,&MainWindow::hide);//当任务完成后,将窗体隐藏
mt->start();
//注意,这里打印的顺序很能说明一个问题:跨线程的信号槽,是在队列中执行的,所以即使emit w.prepared()在主线程打印之前
//实际也是先打印了主线程ID,然后是进了线程对象的run()中打印了子线程ID,再然后是进了子线程的事件循环,
//再之后开始打印任务printW()
//create worker at thread: 0x53e4
// main thread: 0x53e4
// Thread: 0x47d0
// . 0 at thread: 0x47d0
// . 1 at thread: 0x47d0
//...
emit w.prepared();
qDebug()<<"main thread:"<<QThread::currentThreadId();//主线程ID
return a.exec();
}
Worker对象被移动到新的线程中,但是其信号可以安全地连接到主线程中对象的槽函数。Qt会自动使用队列连接,确保信号传递线程安全的。
Qt支持多种信号槽连接类型,在线程编程中尤为重要:
-
直接连接(DirectConnection):槽函数在信号发射的线程中立即调用。
-
队列连接(QueuedConnection):槽函数在接收对象所属线程的事件循环中调用。
-
阻塞队列连接(BlockingQueuedConnection):类似队列连接,但发送线程会阻塞直到槽函数返回。
-
自动连接(AutoConnection):默认方式。如果发送者和接收者在同一线程,使用直接连接;否则使用队列连接。
使用线程池QThreadPool
它是 Qt 中一个极其有用的工具,用于管理线程资源,避免频繁创建和销毁线程的开销,非常适合处理大量可并发的短期任务。
核心概念:QRunnable 和 QThreadPool
线程池的工作流程基于两个核心类:
-
QRunnable
:-
这是一个任务 或工作单元 的抽象基类。它类似于 Java 中的
Runnable
接口。 -
你需要继承这个类并重写其
run()
方法。你的所有耗时代码都写在这个run()
方法里。 -
QRunnable
本身不是QObject
的子类,因此没有内建的信号槽机制。
-
-
QThreadPool
:-
这是线程池管理器 。它是一个全局的单例(也可以通过
new
创建私有实例)。 -
它的主要职责是接受
QRunnable
任务,并将其分配给池中的空闲线程去执行。 -
你可以通过
globalInstance()
获取全局线程池实例。
-
cpp
//task.cpp
Task::Task(int id):m_id{id}
{
}
void Task::run()
{
qDebug() << "Task" << m_id << "started in thread:" << QThread::currentThreadId();
// 模拟耗时操作
QThread::sleep(1);
qDebug() << "Task" << m_id << "finished";
// 注意:这里不能直接操作UI部件!需要通过信号槽(如果与QObject结合)或其它线程安全的方式与主线程通信。
}
//main.cpp
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// 2. 获取全局线程池实例
QThreadPool* pool = QThreadPool::globalInstance();
qDebug() << "Max threads:" << pool->maxThreadCount();
// 3. 创建并提交多个任务
for (int i = 0; i < 10; ++i)
{
Task* task = new Task(i);
pool->start(task); // 线程池会取得任务的所有权(如果autoDelete为true)
QThread::msleep(300);
}
qDebug() << "All tasks submitted. Active threads:" << pool->activeThreadCount();
// 等待所有任务完成(可选)
pool->waitForDone();
qDebug() << "All tasks completed.";
return a.exec();
}
使用QtConcurrent
QtConcurrent
是一个命名空间,提供了一系列用于编写多线程程序 的高级函数。它构建在 QThreadPool
之上,但抽象程度更高,使用起来更加简单和直观。
核心思想:基于函数式编程(Map、Filter、Reduce)模型,让你像调用普通函数一样实现并行操作。
主要优势:
-
极其简单:只需一行代码就能启动并行计算。
-
无需管理线程:完全由 Qt 自动管理线程池和任务调度。
-
安全 :提供了与主线程交互的友好方式(通过
QFuture
和QFutureWatcher
)。 -
功能强大:支持多种并行模式。
核心组件:QFuture 和 QFutureWatcher
在深入 QtConcurrent
函数之前,必须先理解这两个类:
-
QFuture<T>
:-
表示一个异步计算的结果。你可以把它想象成一个"未来的值"。
-
它提供了方法来查询计算状态(
isStarted()
,isFinished()
,isCanceled()
)、等待结果(waitForFinished()
)以及获取结果(result()
,results()
)。 -
它是由
QtConcurrent
函数返回的。
-
-
QFutureWatcher<T>
:-
用于监视 一个
QFuture
对象。它不是必须的,但如果你想在 GUI 程序中获得进度通知或完成信号,就必须使用它。 -
它提供了信号(
started()
,finished()
,progressValueChanged()
,progressRangeChanged()
)来通知你计算的状态变化。 -
让你能够在不阻塞主线程的情况下与异步计算交互。
-
cpp
//main.cpp
void workFunc()
{
//一个耗时的任务
QThread::sleep(1);
qDebug()<<"work at thread:"<<QThread::currentThreadId();
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QFuture<void> future = QtConcurrent::run(workFunc);
QThread::sleep(3);
qDebug()<<"main thread:"<<QThread::currentThreadId();
return a.exec();
}
使用watcher来监控结果,并通过信号槽的方式告知结果
cpp
//main.cpp
int workFunc()
{
//一个耗时的任务
QThread::sleep(2);
qDebug()<<"work at thread:"<<QThread::currentThreadId();
return 0;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QFuture<int> future = QtConcurrent::run(workFunc);
//使用watcher来监控结果,并通过信号槽的方式告知结果
QFutureWatcher<int> *watcher = new QFutureWatcher<int>();
QObject::connect(watcher, &QFutureWatcher<int>::finished, &a, [watcher]() {
int res = watcher->future().result();
qDebug() << "res:" << res;
watcher->deleteLater();
});
watcher->setFuture(future);
QThread::sleep(1);
qDebug()<<"main thread:"<<QThread::currentThreadId();
return a.exec();
}