Я пытаюсь создать загрузчик, который будет создавать новые потоки, и в каждом потоке у меня есть QNetworkAccessManager. Все потоки загрузки имеют ссылку на общий список и будут разделять его, используя начальный и конечный индексы.
Загрузчик выглядит примерно так:
class FileUploader : public QObject {
Q_OBJECT
public:
explicit FileUploader(QList<FileInfoWrapper> &fileList, const int start = 0, const int offset = 0, QObject *parent = 0);
void uploadNext();
QString containerName;
private:
int start_, offset_, iterator_;
QList<FileInfoWrapper> &fileList_;
RestFileUploader *restFileUploader;
signals:
void progressChangedAt(int row);
void statusChangedAt(int row);
void finished();
public slots:
void init();
private slots:
void setUploadProgress(qint64 tranfered);
void handleRequestFinished(QNetworkReply* reply);
void handleSslErros(QNetworkReply *reply, const QList<QSslError> &errors);
void handleNetworkError(QNetworkReply::NetworkError error);
};
Затем в функции run() я создаю новый RestFileUploader(this) (почти объект, который создает свой собственный новый QNetworkAccessManager(this) и помещает в него запросы), так что в конструкторе ничего не создается (что может привести к оказаться в основном потоке вместо этого?). Функция запуска создает запрос, который будет передан QNetworkAccessManager, а затем ничего не делает, пока это не сигнализирует "завершено (QNetworkReply)", а затем я беру следующий (и так далее, пока список не будет пройден).
Затем я создаю два новых потока в основном приложении, и когда я запускаю их с помощью run(), он работает, за исключением того, что идентификатор одинаков для обоих потоков. Если я вместо этого вызову «start()», он вылетит с ошибкой: QObject: Невозможно создать дочерние элементы для родителя, который находится в другом потоке. (Родительский поток — FileUploader (0x2580748), родительский поток — QThread (0x4fb2b8), текущий поток — FileUploader (0x2580748)
НО! Непосредственно перед тем, как начать просмотр списка, я печатаю threadId, и они уже не совпадают.
Что я делаю не так или мне просто нужно сделать это: http://labs.qt.nokia.com/2006/12/04/threading-without-the-headache/ ?
Редактировать:
Я изменил его и переименовал run в start и сделал эту обертку (и я больше не вызываю NetworkAccessManager или RestFileUploader с помощью «этого»):
FileUploader *fileUploader = new FileUploader(fileList_, start, (offset == 0 ? (fileList_.count() - start) : offset));
QThread *fileUploaderThread = new QThread;
fileUploader->moveToThread(fileUploaderThread);
connect(fileUploader, SIGNAL(progressChangedAt(int)), model_, SLOT(reportProgressChanged(int)));
connect(fileUploader, SIGNAL(statusChangedAt(int)), model_, SLOT(reportStatusChanged(int)));
fileUploaderThread->start();
QMetaObject::invokeMethod(fileUploader, "init", Qt::QueuedConnection);
При загрузке одного объекта работает, так как тогда я использую только один поток. Но когда у меня появилось больше объектов, которые я разделил, приложение резко упало с этим сообщением об ошибке:
ASSERT failure in QMutexLocker: "QMutex pointer is misaligned", file ..\..\include/QtCore/../../../../../../ndk_buildrepos/qt-desktop/src/corelib/thread/qmutex.h, line 100
Invalid parameter passed to C runtime function.
Invalid parameter passed to C runtime function.
Пожалуйста помогите
Редактировать:
загрузчик файлов.cpp
#include "fileuploader.h"
FileUploader::FileUploader(QList<FileInfoWrapper> &fileList, const int start, const int offset, QObject *parent)
: QObject(parent), start_(start), offset_(offset), iterator_(start - 1), fileList_(fileList) {
}
void FileUploader::init() {
restFileUploader = new RestFileUploader();
connect(restFileUploader, SIGNAL(uploadProgress(qint64)), this, SLOT(setUploadProgress(qint64)));
connect(restFileUploader, SIGNAL(requestFinished(QNetworkReply*)), this, SLOT(handleRequestFinished(QNetworkReply*)));
connect(restFileUploader, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)), this, SLOT(handleSslErros(QNetworkReply*,QList<QSslError>)));
connect(restFileUploader, SIGNAL(networkError(QNetworkReply::NetworkError)), this, SLOT(handleNetworkError(QNetworkReply::NetworkError)));
containerName = "temp"
qDebug() << "thread" << this->thread()->currentThreadId() << start_ << ":" << offset_;
uploadNext();
}
void FileUploader::uploadNext() {
qDebug() << "uploadNext" << this->thread()->currentThreadId();
if((iterator_ + 1) < (start_ + offset_)) {
iterator_++;
restFileUploader->putBlob(containerName, fileList_.at(iterator_).fileName(), fileList_.at(iterator_).fileInfo().filePath());
} else emit finished();
}
void FileUploader::setUploadProgress(qint64 tranfered) {
fileList_[iterator_].setProgress(tranfered);
emit progressChangedAt(iterator_);
}
void FileUploader::handleRequestFinished(QNetworkReply* reply) {
qDebug() << "finished blob: " << iterator_ << " in thread " << this->thread()->currentThreadId();
if(reply->error() > QNetworkReply::NoError) {
qDebug() << reply->errorString();
fileList_[iterator_].uploadFailed();
emit progressChangedAt(iterator_);
} else fileList_[iterator_].uploadFinished();
emit statusChangedAt(iterator_);
uploadNext();
}
void FileUploader::handleNetworkError(QNetworkReply::NetworkError error) {
if(error > QNetworkReply::NoError) {
fileList_[iterator_].uploadFailed();
restFileUploader->cancelCurrentRequest();
emit progressChangedAt(iterator_);
emit statusChangedAt(iterator_);
}
}
void FileUploader::handleSslErros(QNetworkReply *reply, const QList<QSslError> &errors) {
if(reply->error() > QNetworkReply::NoError) {
qDebug() << reply->errorString();
fileList_[iterator_].uploadFailed();
restFileUploader->cancelCurrentRequest();
emit progressChangedAt(iterator_);
emit statusChangedAt(iterator_);
}
}
#include "restfileuploader.h"
void RestFileUploader::putBlob(const QString& container, const QString& blob, const QString& filePath) {
QFile *uploadFile = new QFile(filePath, this); // <--- this maybe?
uploadFile->open(QIODevice::ReadOnly);
QNetworkRequest request = this->createRestRequest("PUT", QString("%1/%2").arg(container, blob), uploadFile->size(), headers);
reply_ = accessManager_->put(request, uploadFile);
connect(reply_, SIGNAL(uploadProgress(qint64, qint64)), this, SLOT(reportUploadProgress(qint64, qint64)));
connect(reply_, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(reportNetworkError(QNetworkReply::NetworkError)));
qDebug() << this->thread()->currentThreadId();
}
void RestFileUploader::cancelCurrentRequest() {
reply_->abort();
}
RestFileUploader::~RestFileUploader() {
qDebug() << "RestFileUploader deleted";
reply_->deleteLater();
}
Итак... 1 тред с одной вещью для загрузки == ок. 2 объекта на двух потоках тоже в порядке. Когда я пытаюсь загрузить 3 или более объектов в два потока, все идет к чертям.
Кроме того, может ли это быть связано с тем, что пользовательский интерфейс считывает информацию о файлах одновременно с ее изменением?
РЕДАКТИРОВАТЬ: по какой-то причине мое приложение теперь работает в 4.8.0, когда я компилирую его в визуальной студии. Может это как-то связано с версией 4.7.4?
run()
напрямую; это то, что будет делать новый поток послеstart()
ed. Да, я знаю, что это тоже не работает для вас (следовательно, это комментарий, а не ответ), ноrun()
действительно не следует вызывать из основного потока. - person Donal Fellows   schedule 27.02.2012