Qt线程池使用flaviotordini/http库无法接收消息的问题解决
解决Qt线程池结合flaviotordini/http库无法接收HTTP请求结果的问题
问题场景
在使用Qt线程池(QThreadPool)搭配flaviotordini/http库发起HTTP请求时,出现以下问题:
- 线程任务(
SpiderTask,继承自QRunnable和QObject)的run()函数中,发起HTTP请求后连接HttpReply::finished信号,但槽函数从未执行,无法获取请求结果。
线程池任务代码
SpiderTask::SpiderTask(int id, QString url,QObject *parent) : QObject{parent},QRunnable() { this->url = url; this->id = id; setAutoDelete(true); } void SpiderTask::run(){ QCoreApplication::processEvents(); int rand = QRandomGenerator::global()->bounded(0,10); QThread::sleep(rand); auto reply = Http::instance().get("https://www.baidu.com/s?wd=" + this->url); connect(reply, &HttpReply::finished, this, [=](auto &reply) { if (reply.isSuccessful()) { emit recvState(this->id, reply.body()); } else { qDebug() << "Something's wrong here" << reply.statusCode() << reply.reasonPhrase(); } qDebug() << reply.body(); }); }
主执行代码
QThreadPool::globalInstance()->setMaxThreadCount(ui->threadVloumelineEdit->text().toInt()); int rows = ui->spiderTableView->model()->rowCount(); for(int r = 0; r < rows; r++) { QString url = ui->spiderTableView->model()->index(r,0).data().toString(); SpiderTask *t = new SpiderTask(r,url); connect(t,&SpiderTask::recvState,this,&MainWindow::changeSpiderState); QThreadPool::globalInstance()->start(t); }
问题分析
对象提前销毁:
SpiderTask设置了setAutoDelete(true),线程池会在run()函数执行完毕后立即销毁该任务对象。但Http::get()是异步请求,finished信号要等请求完成后才触发,此时SpiderTask已被销毁,信号与槽的连接失效,槽函数无法执行。线程无事件循环:
Qt网络操作依赖事件循环处理异步回调,线程池中的线程默认没有运行事件循环。run()函数执行完毕后线程直接退出,HTTP请求的异步回调无法被处理,导致finished信号无法正常发射。
解决方案
方案一:在run()中启动局部事件循环,等待请求完成
修改SpiderTask的逻辑,关闭自动销毁,启动事件循环等待请求完成后再手动销毁对象:
SpiderTask::SpiderTask(int id, QString url, QObject *parent) : QObject{parent}, QRunnable() { this->url = url; this->id = id; // 关闭自动销毁,手动控制生命周期 setAutoDelete(false); } void SpiderTask::run(){ int rand = QRandomGenerator::global()->bounded(0,10); QThread::sleep(rand); QEventLoop loop; auto reply = Http::instance().get("https://www.baidu.com/s?wd=" + this->url); connect(reply, &HttpReply::finished, this, [=](auto &reply) { if (reply.isSuccessful()) { emit recvState(this->id, reply.body()); } else { qDebug() << "请求失败" << reply.statusCode() << reply.reasonPhrase(); } qDebug() << reply.body(); // 请求处理完毕,退出事件循环 loop.quit(); }); // 启动事件循环,等待请求完成 loop.exec(); // 手动销毁任务对象 delete this; }
方案二:改用QObject+moveToThread模式(可选)
如果更倾向Qt的线程对象模型,可将任务逻辑放在QObject派生类中,确保线程有事件循环处理异步回调:
class SpiderWorker : public QObject { Q_OBJECT public slots: void doWork(int id, const QString &url) { int rand = QRandomGenerator::global()->bounded(0,10); QThread::sleep(rand); auto reply = Http::instance().get("https://www.baidu.com/s?wd=" + url); connect(reply, &HttpReply::finished, this, [=](auto &reply) { if (reply.isSuccessful()) { emit recvState(id, reply.body()); } else { qDebug() << "请求失败" << reply.statusCode() << reply.reasonPhrase(); } emit workDone(); }); } signals: void recvState(int id, const QString &body); void workDone(); }; // 主函数调用逻辑 QThreadPool::globalInstance()->setMaxThreadCount(ui->threadVloumelineEdit->text().toInt()); int rows = ui->spiderTableView->model()->rowCount(); for(int r = 0; r < rows; r++) { QString url = ui->spiderTableView->model()->index(r,0).data().toString(); SpiderWorker *worker = new SpiderWorker; connect(worker, &SpiderWorker::recvState, this, &MainWindow::changeSpiderState); connect(worker, &SpiderWorker::workDone, worker, &SpiderWorker::deleteLater); // 在线程池线程中执行任务并启动事件循环 QThreadPool::globalInstance()->start([=](){ worker->doWork(r, url); QEventLoop loop; connect(worker, &SpiderWorker::workDone, &loop, &QEventLoop::quit); loop.exec(); }); }
关键注意点
- 方案一中必须关闭
setAutoDelete(true),避免对象在请求完成前被销毁。 - 无论哪种方案,都要确保线程运行事件循环,否则异步网络回调无法被处理。
内容的提问来源于stack exchange,提问作者Hao Tan
相关产品推荐
相关产品推荐

