Im trying to create an uploader that will create new threads and in every thread I have a QNetworkAccessManager. All the uploader threads have a reference to a shared list and will split it by using start and end indexes.
The Uploader looks something like this:
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);
};
Then, in the run() function I create the new RestFileUploader(this)(Pretty much a object which creates its own new QNetworkAccessManager(this) and put requests on it) so that nothing is created in the constructor(which would cause it to end up in the main thread instead?). The run function creates a request to be given to the QNetworkAccessManager and then does nothing until that signals "finished(QNetworkReply)" and then I grab the next one (and so on until the list is stepped through).
Then I create two new threads in the main application and when I start them using run() it works except that the ID is the same on both the threads. If I instead call "start()" it crashes with: QObject: Cannot create children for a parent that is in a different thread. (Parent is FileUploader(0x2580748), parent's thread is QThread(0x4fb2b8), current thread is FileUploader(0x2580748)
BUT! Just before I start step through the list, I print the threadId and they are no longer the same.
What am I doing wrong or should I just do this: http://labs.qt.nokia.com/2006/12/04/threading-without-the-headache/ ?
Edit:
I changed it and renamed run to start and made this wrapper(and I no longer call NetworkAccessManager or RestFileUploader with "this"):
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);
When uploading one object works, since I only use one thread then. But when I have more objects that I split up, the application crashed brutally with this error message:
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.
Please help me
Edit:
fileuploader.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();
}
So... 1 thread with one thing to upload == ok. 2 objects on two threads are also okey. When I try to upload 3 or more objects on the two threads it all goes to hell.
Also, could it have something to do with that the UI is reading the info of the files at the same time as I change it?
EDIT: For some reason my app now works in 4.8.0 when I compile it in visual studio. Could it have something to do with version 4.7.4?
run()
directly; that's what the new thread will do oncestart()
ed. Yes, I know that's not working for you either (hence this is a comment, not an answer) butrun()
really shouldn't be called from the main thread. – Somerset