研究内容:
学习爬取网络数据的工程,但是性能上想达到并发效果,一直做线程,没有做线程池并发效果。
处理中发现:
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QNetworkAccessManager(0x21dcba0), parent's thread is QThread(0x20be1c0), current thread is QThread(0x7f48e0044730)
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*)));
manager->get(QNetworkRequest(QUrl("http://qt-project.org")));
参考的这篇文章:
https://www.cnblogs.com/btian/p/6512365.html
使用线程的movethread就不谈了。但是并发的时候我没有想到好的框架,我想用线程池的方式来处理并发问题,因为一个页面一个页面去爬取,5000条要等好久的。
我本次是第一次写这个并发的模型,可能还需要调整。
上面的问题通俗点理解就是在线程里操作网络的时,线程里面创建的对象是子类的,不可以将父类指针在两者之间公用。就是那个THIS。
原来的模型:
class MyNetwork: public QObject
{
Q_OBJECT
public:
explicit MyNetwork(QObject *parent = nullptr);
~MyNetwork();
void getHtml(QString url, int id, QObject *);
int m_id;
QString m_url;
public slots:
void replyFinished(QNetworkReply *reply);
void slotReadyRead();
void slotError(QNetworkReply::NetworkError);
void dowork();
private:
QNetworkAccessManager *m_network;
QNetworkReply *reply;
QNetworkRequest * m_request;
signals:
void getid(int id,QString msg);
};
#endif // MYNETWORK_H
主程序通过线程调用的话:(初学者最好用movethread,为什么?我就是初学者,因为初学者用这个问题比较少,讲太多可能也不方便去感受线程的魅力,那么,可以针对这个问题再深入去研究)
QThread *thread = new QThread();
MyNetwork *my = new MyNetwork ();//自己考虑去释放
my->moveToThread(thread);
thread->start();
connect(thread, SIGNAL(started()), my, SLOT(dowork()));
以上:是调用单个线程的,如果有几十个左右的网络请求,一个线程FOR循环多次请求,或多开几个线程都是满足项目需求。
///
多个线程考虑了线程池,然后就想把上面的改成并发的效果,结果可想而知(Parent is QNetworkAccessManager(0x21dcba0), parent's thread is QThread(0x20be1c0), current thread is QThread(0x7f48e0044730)
我在线程里实例化QNetworkAccessManager,信号槽完全没反应,又是一个安静的深夜,搞的我头昏昏的。
这里面有设计到一个知识点
参考文章 :https://blog.csdn.net/xiangjai/article/details/52091544
======================================
此情况下finished信号永远不被触发,在线程外面调用则可正常触发使用,通过查资料终于弄明白,
放在线程内使用QNetworkAccessManager时没有进行线程的事件循环,需要调用exec()才能开启线程的事件循环,
因此解决方式为:get(request)后面加上exec(),使线程进行事件循环。
但是通常很多人还是加不上去。
================================
#include
#include
#include
class URLTaskWorker:public QObject, public QRunnable
{
Q_OBJECT
public:
URLTaskWorker(){
}
public:
public slots:
void run(){
QEventLoop temp_loop;
QNetworkAccessManager qnam1 ;
this->url = QUrl("http://www.baidu.com");
QNetworkRequest request(url);
reply=qnam1.get(request);
connect(reply, SIGNAL(finished()), &temp_loop, SLOT(quit()));
temp_loop.exec();
reply->waitForReadyRead(3);
qDebug()<<"start";
if(reply->error()==QNetworkReply::NoError) //这个部分如果能信号出去就更好了,后续再研究
{
QByteArray bytes = reply->readAll();
qDebug()<<bytes.size();
}
qDebug()<<"finished";
reply->deleteLater();
qDebug() << url <<reply;
};
private:
QNetworkAccessManager qnam;
QUrl url;
QNetworkReply* reply;
};
#endif // URLTASKWORKER_H
主程序通过线程池调用,达到我的效果:
QThreadPool pool;
pool.setMaxThreadCount(5);
for(int i = 0; i < m_list.size(); i++)//5000条数据多线程爬取
{
URLTaskWorker * net2 = new URLTaskWorker();
pool.start( net2);
}