使用 QNetworkAccessManager 时启动 QThread 失败
Failed to start QThread when using QNetworkAccessManager
我目前正在尝试制作一个可以从 Google Drive 下载大量文件的软件。下载目前没有问题。
然而,我在同时启动 500 多个下载时遇到了问题。我使用本教程的略微修改版本:https://wiki.qt.io/Download_Data_from_URL.
这是 .h 文件:
class FileDownloader : public QObject
{
Q_OBJECT
public:
explicit FileDownloader(QUrl url, QObject *parent = 0, int number = 0);
QByteArray downloadedData() const;
void launchNewDownload(QUrl url);
QByteArray m_DownloadedData;
QNetworkReply* reply;
static QNetworkAccessManager *m_WebCtrl;
signals:
void downloaded();
private slots:
void fileDownloaded(QNetworkReply* pReply);
};
这是 .cpp 文件:
QNetworkAccessManager* FileDownloader::m_WebCtrl = nullptr;
FileDownloader::FileDownloader(QUrl url, QObject *parent) :
QObject(parent)
{
if (m_WebCtrl == nullptr) {
m_WebCtrl = new QNetworkAccessManager(this);
}
connect(m_WebCtrl, SIGNAL (finished(QNetworkReply*)),this, SLOT (fileDownloaded(QNetworkReply*)));
launchNewDownload(url);
}
void FileDownloader::launchNewDownload(QUrl url) {
QNetworkRequest request(url);
this->reply = m_WebCtrl->get(request);
}
void FileDownloader::fileDownloaded(QNetworkReply* pReply) {
m_DownloadedData = pReply->readAll();
//emit a signal
pReply->deleteLater();
emit downloaded();
}
QByteArray FileDownloader::downloadedData() const {
return m_DownloadedData;
}
问题是 "QThread::start: Failed to create thread ()" 达到大约第 500 次下载时。我试图限制同时 运行 的下载数量 - 但我总是遇到同样的问题。此外,我试图在完成任务时删除每个下载器 - 它除了使程序崩溃外什么也没做 ;)
我认为它来自于唯一进程允许的线程数,但我无法解决它!
有没有人有想法可以帮助我?
谢谢!
QNetworkAccessManager::finished
信号被记录为 待处理的网络回复完成时发出 。
这意味着如果QNetworkAccessManager
用于一次运行多个请求(这是perfectly normal usage)。 finished
每个请求都会发出一次信号。由于您的 FileDownloader
对象之间有一个 QNetworkAccessManager
的共享实例,因此您发出的每个 get
调用都会发出 finished
信号。因此,第一个 FileDownloader
完成下载后,所有 FileDownloader
对象都会收到 finished
信号。
您可以使用 QNetworkReply::finished
而不是使用 QNetworkAccessManager::finished
,以避免混淆信号。这是一个示例实现:
#include <QtNetwork>
#include <QtWidgets>
class FileDownloader : public QObject
{
Q_OBJECT
//using constructor injection instead of a static QNetworkAccessManager pointer
//This allows to share the same QNetworkAccessManager
//object with other classes utilizing network access
QNetworkAccessManager* m_nam;
QNetworkReply* m_reply;
QByteArray m_downloadedData;
public:
explicit FileDownloader(QUrl imageUrl, QNetworkAccessManager* nam,
QObject* parent= nullptr)
:QObject(parent), m_nam(nam)
{
QNetworkRequest request(imageUrl);
m_reply = m_nam->get(request);
connect(m_reply, &QNetworkReply::finished, this, &FileDownloader::fileDownloaded);
}
~FileDownloader() = default;
QByteArray downloadedData()const{return m_downloadedData;}
signals:
void downloaded();
private slots:
void fileDownloaded(){
m_downloadedData= m_reply->readAll();
m_reply->deleteLater();
emit downloaded();
}
};
//sample usage
int main(int argc, char* argv[]){
QApplication a(argc, argv);
QNetworkAccessManager nam;
FileDownloader fileDownloader(QUrl("http://i.imgur.com/Rt8fqpt.png"), &nam);
QLabel label;
label.setAlignment(Qt::AlignCenter);
label.setText("Downloading. . .");
label.setMinimumSize(640, 480);
label.show();
QObject::connect(&fileDownloader, &FileDownloader::downloaded, [&]{
QPixmap pixmap;
pixmap.loadFromData(fileDownloader.downloadedData());
label.setPixmap(pixmap);
});
return a.exec();
}
#include "main.moc"
如果您使用此方法下载大文件,请考虑查看 。
一种解决方案是使用 QThreadPool。您只需向它提供任务 (QRunnable),它就会为您处理线程数和任务队列。
然而,在您的情况下,这并不完美,因为您会将同时下载的数量限制为 QThreadPool 创建的线程数量(通常是您拥有的 CPU 核心数量)。
要克服这个问题,您必须自己处理 QThread 而不是使用 QThreadPool。您将在每个 QThread 上创建少量线程(参见 QThread::idealThreadCount()
)和 运行 多个 FileDownloader
。
我目前正在尝试制作一个可以从 Google Drive 下载大量文件的软件。下载目前没有问题。
然而,我在同时启动 500 多个下载时遇到了问题。我使用本教程的略微修改版本:https://wiki.qt.io/Download_Data_from_URL.
这是 .h 文件:
class FileDownloader : public QObject
{
Q_OBJECT
public:
explicit FileDownloader(QUrl url, QObject *parent = 0, int number = 0);
QByteArray downloadedData() const;
void launchNewDownload(QUrl url);
QByteArray m_DownloadedData;
QNetworkReply* reply;
static QNetworkAccessManager *m_WebCtrl;
signals:
void downloaded();
private slots:
void fileDownloaded(QNetworkReply* pReply);
};
这是 .cpp 文件:
QNetworkAccessManager* FileDownloader::m_WebCtrl = nullptr;
FileDownloader::FileDownloader(QUrl url, QObject *parent) :
QObject(parent)
{
if (m_WebCtrl == nullptr) {
m_WebCtrl = new QNetworkAccessManager(this);
}
connect(m_WebCtrl, SIGNAL (finished(QNetworkReply*)),this, SLOT (fileDownloaded(QNetworkReply*)));
launchNewDownload(url);
}
void FileDownloader::launchNewDownload(QUrl url) {
QNetworkRequest request(url);
this->reply = m_WebCtrl->get(request);
}
void FileDownloader::fileDownloaded(QNetworkReply* pReply) {
m_DownloadedData = pReply->readAll();
//emit a signal
pReply->deleteLater();
emit downloaded();
}
QByteArray FileDownloader::downloadedData() const {
return m_DownloadedData;
}
问题是 "QThread::start: Failed to create thread ()" 达到大约第 500 次下载时。我试图限制同时 运行 的下载数量 - 但我总是遇到同样的问题。此外,我试图在完成任务时删除每个下载器 - 它除了使程序崩溃外什么也没做 ;)
我认为它来自于唯一进程允许的线程数,但我无法解决它!
有没有人有想法可以帮助我?
谢谢!
QNetworkAccessManager::finished
信号被记录为 待处理的网络回复完成时发出 。
这意味着如果QNetworkAccessManager
用于一次运行多个请求(这是perfectly normal usage)。 finished
每个请求都会发出一次信号。由于您的 FileDownloader
对象之间有一个 QNetworkAccessManager
的共享实例,因此您发出的每个 get
调用都会发出 finished
信号。因此,第一个 FileDownloader
完成下载后,所有 FileDownloader
对象都会收到 finished
信号。
您可以使用 QNetworkReply::finished
而不是使用 QNetworkAccessManager::finished
,以避免混淆信号。这是一个示例实现:
#include <QtNetwork>
#include <QtWidgets>
class FileDownloader : public QObject
{
Q_OBJECT
//using constructor injection instead of a static QNetworkAccessManager pointer
//This allows to share the same QNetworkAccessManager
//object with other classes utilizing network access
QNetworkAccessManager* m_nam;
QNetworkReply* m_reply;
QByteArray m_downloadedData;
public:
explicit FileDownloader(QUrl imageUrl, QNetworkAccessManager* nam,
QObject* parent= nullptr)
:QObject(parent), m_nam(nam)
{
QNetworkRequest request(imageUrl);
m_reply = m_nam->get(request);
connect(m_reply, &QNetworkReply::finished, this, &FileDownloader::fileDownloaded);
}
~FileDownloader() = default;
QByteArray downloadedData()const{return m_downloadedData;}
signals:
void downloaded();
private slots:
void fileDownloaded(){
m_downloadedData= m_reply->readAll();
m_reply->deleteLater();
emit downloaded();
}
};
//sample usage
int main(int argc, char* argv[]){
QApplication a(argc, argv);
QNetworkAccessManager nam;
FileDownloader fileDownloader(QUrl("http://i.imgur.com/Rt8fqpt.png"), &nam);
QLabel label;
label.setAlignment(Qt::AlignCenter);
label.setText("Downloading. . .");
label.setMinimumSize(640, 480);
label.show();
QObject::connect(&fileDownloader, &FileDownloader::downloaded, [&]{
QPixmap pixmap;
pixmap.loadFromData(fileDownloader.downloadedData());
label.setPixmap(pixmap);
});
return a.exec();
}
#include "main.moc"
如果您使用此方法下载大文件,请考虑查看
一种解决方案是使用 QThreadPool。您只需向它提供任务 (QRunnable),它就会为您处理线程数和任务队列。
然而,在您的情况下,这并不完美,因为您会将同时下载的数量限制为 QThreadPool 创建的线程数量(通常是您拥有的 CPU 核心数量)。
要克服这个问题,您必须自己处理 QThread 而不是使用 QThreadPool。您将在每个 QThread 上创建少量线程(参见 QThread::idealThreadCount()
)和 运行 多个 FileDownloader
。