解决qt子线程中循环操作导致的无法按顺序执行导致的并发问题

2023年11月6日 19:22 ry 716

最近一直在开发一个qt的项目,目前我有一个这样的需求,就是要网络请求一个账号的数据,必须经过上一次请求的一个数据结果当做下一个请求翻页的参数,而第一次请求的参数默认是0,因此如果要请求完所有的数据,必须不断循环,因此我决定用qt来实现,开始我的代码如下

void Scraping::getRequestWorking(QString id,QStringList list)
{
    if(id !="")
    {
        for(int page=0;page<2;page++)
        {
            qDebug()<<id;
            QString link = "https:xxx/id="+id+"&count=20&offset="+offset";
            qDebug()<<link;
            QUrl url(link);
            QNetworkRequest request(url);


            QNetworkAccessManager *manager = new QNetworkAccessManager(this);
            QNetworkReply* reply = manager->get(request);
            //存储返回的数据包响应
            QByteArray *responseDataAll = new QByteArray();
            // 连接QNetworkReply的finished信号到槽函数

            connect(reply, &QNetworkReply::finished, this, [=]() {
                //将字符转为json格式
                *responseDataAll = reply->readAll();

                QJsonDocument responseDocument = QJsonDocument::fromJson(*responseDataAll);
                QJsonObject responseObject = responseDocument.object();
                //获取翻页的offset
                long long int offset_temp = responseObject.value("offset").toVariant().toLongLong();
                qDebug()<<offset_temp;
                offset = QString::number(offset_temp);
                qDebug()<<"offset:"<<offset;

                QJsonArray jsonArray = responseObject.value("data").toArray();

                for(int ii=0;ii<jsonArray.size();ii++)
                {

                    QString contentData =  jsonArray.at(ii).toObject().value("content").toString();
                    QJsonDocument contentDocument = QJsonDocument::fromJson(contentData.toUtf8());
                    QJsonObject contentObject = contentDocument.object();
                    QString title = contentObject.value("title").toString();//标题
                    int readNum = contentObject.value("read_count").toInt();//阅读量
                    QString source = contentObject.value("source").toString();//来源
                    int comment_count = contentObject.value("comment_count").toInt();//评论量
                    int like_count = contentObject.value("like_count").toInt();//点赞量
                    int share_count = contentObject.value("share_count").toInt();//转发量
                    qint64 behot_time = contentObject.value("behot_time").toVariant().toLongLong();
                    //将时间戳转为年月日时分秒格式
                    QDateTime dataTime = QDateTime::fromSecsSinceEpoch(behot_time);
                    //将格式转为字符串格式
                    QString formatterDateTime = dataTime.toString("yyyy-MM-dd hh:mm");
                    qDebug()<<title;
                    qDebug()<<readNum;
                    qDebug()<<source;
                    qDebug()<<comment_count;
                    qDebug()<<like_count;
                    qDebug()<<share_count;
                    qDebug()<<behot_time;
                    qDebug()<<formatterDateTime;

                }

                // 在这里处理返回的数据
                reply->deleteLater(); // 释放reply对象

            });

        }


    }
      else
    {
        qDebug() << "数据不为空";
    }

}

注意,这里的函数getRequestWorking是一个子线程函数,在widget.cpp中已经声明了,

 scrapingThread = new QThread(this);
    //创建采集任务对象
    Scraping *scrapy = new Scraping();
    //将任务对象移动到子线程
    scrapy->moveToThread(scrapingThread);
    //主线程发送给采集子线程的参数
    connect(this,&Widget::startScrap,scrapy,&Scraping::getRequestWorking);
    //子线程将采集的数据发送给主线程处理
    connect(scrapy,&Scraping::sendScrapData,this,&Widget::receiveData);

因此在子线程中循环进行网络请求通过更新this->offset值来请求新的数据好像是可行的,注意这里的offset在scraping.h中进行了声明,如下所示

因此按照我们的理解在子线程就可以不断更新这个offset来请求新的数据,发现运行后page=0,和page=1的link值一样,也就是offset一直都是0,究竟是为什么呢,发现了下,由于网络请求返回的数据巨大,在子线程中花费时间过多导致任务并发执行了,并没用按照顺序执行,因此无法更新offset的值。那么如何解决呢,我们可以通过QEventLoop事件循环来等待它执行完网络请求更新完offset后即可,完整的代码如下所示

void Scraping::getRequestWorking(QString id,QStringList list)
{
    if(id !="")
    {
        for(int page=0;page<2;page++)
        {
            qDebug()<<id;
            QString link = "https:xxx/id="+id+"&count=20&offset="+offset";
            qDebug()<<link;
            QUrl url(link);
            QNetworkRequest request(url);


            QNetworkAccessManager *manager = new QNetworkAccessManager(this);
            QNetworkReply* reply = manager->get(request);
            //存储返回的数据包响应
            QByteArray *responseDataAll = new QByteArray();
            // 连接QNetworkReply的finished信号到槽函数
            QEventLoop *eventLoop = new QEventLoop();
            connect(reply, &QNetworkReply::finished, this, [=]() {
                //将字符转为json格式
                *responseDataAll = reply->readAll();

                QJsonDocument responseDocument = QJsonDocument::fromJson(*responseDataAll);
                QJsonObject responseObject = responseDocument.object();
                //获取翻页的offset
                long long int offset_temp = responseObject.value("offset").toVariant().toLongLong();
                qDebug()<<offset_temp;
                offset = QString::number(offset_temp);
                qDebug()<<"offset:"<<offset;

                QJsonArray jsonArray = responseObject.value("data").toArray();

                for(int ii=0;ii<jsonArray.size();ii++)
                {

                    QString contentData =  jsonArray.at(ii).toObject().value("content").toString();
                    QJsonDocument contentDocument = QJsonDocument::fromJson(contentData.toUtf8());
                    QJsonObject contentObject = contentDocument.object();
                    QString title = contentObject.value("title").toString();//标题
                    int readNum = contentObject.value("read_count").toInt();//阅读量
                    QString source = contentObject.value("source").toString();//来源
                    int comment_count = contentObject.value("comment_count").toInt();//评论量
                    int like_count = contentObject.value("like_count").toInt();//点赞量
                    int share_count = contentObject.value("share_count").toInt();//转发量
                    qint64 behot_time = contentObject.value("behot_time").toVariant().toLongLong();
                    //将时间戳转为年月日时分秒格式
                    QDateTime dataTime = QDateTime::fromSecsSinceEpoch(behot_time);
                    //将格式转为字符串格式
                    QString formatterDateTime = dataTime.toString("yyyy-MM-dd hh:mm");
                    qDebug()<<title;
                    qDebug()<<readNum;
                    qDebug()<<source;
                    qDebug()<<comment_count;
                    qDebug()<<like_count;
                    qDebug()<<share_count;
                    qDebug()<<behot_time;
                    qDebug()<<formatterDateTime;

                }

                // 在这里处理返回的数据
                reply->deleteLater(); // 释放reply对象
                eventLoop->quit(); // 停止事件循环
            });
             eventLoop->exec(); // 启动事件循环,等待请求完成

        }


    }
      else
    {
        qDebug() << "数据不为空";
    }

}

每次page的for循环后面设置启动事件循环,在connect最后停止事件循环即可实现效果!

如果上述代码帮助您很多,可以打赏下以减少服务器的开支吗,万分感谢!

欢迎发表评论~

点击此处登录后即可评论


评论列表
2023年11月17日 14:33 ry: 回复
本人承接各种软件定制,数据采集,接口搭建,网站开发,环境配置的杂活,需要的可以联系我 qq:1449917271 微信:liuyoudyping


赣ICP备2021001574号-1

赣公网安备 36092402000079号