Ошибка QThread ASSERT в QMutexLocker: «Указатель QMutex смещен», - PullRequest
1 голос
/ 27 февраля 2012

Я пытаюсь создать загрузчик, который будет создавать новые потоки, и в каждом потоке у меня есть 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 (это) и помещает запросы в него), чтобы в конструкторе ничего не создавалось (что могло бы привести к тому, что он оказался бы в основном потоке?).Функция run создает запрос, который должен быть передан QNetworkAccessManager, а затем ничего не делает до тех пор, пока не появится сигнал «Finished (QNetworkReply)», а затем я перехватываю следующий (и так далее, пока список не будет пройден).

Затем я создаю два новых потока в основном приложении, и когда я запускаю их с помощью run (), это работает, за исключением того, что идентификатор одинаков для обоих потоков.Если я вместо этого вызываю «start ()», то происходит сбой с помощью: QObject: Невозможно создать дочерние элементы для родителя, который находится в другом потоке.(Родитель - FileUploader (0x2580748), родительский поток - QThread (0x4fb2b8), текущий поток - FileUploader (0x2580748)

НО! Непосредственно перед тем, как начать пошаговый просмотр списка, я печатаю threadId, и они больше не являютсято же самое.

Что я делаю не так или я должен просто сделать это: http://labs.qt.nokia.com/2006/12/04/threading-without-the-headache/?

Редактировать:

Я изменил его и переименовал в Run для запуска исделал эту обертку (и я больше не вызываю NetworkAccessManager или RestFileUploader с «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);

При загрузке одного объекта работает, так как тогда я использую только один поток. Но когда у меня больше объектов, которые я разделяювверх, приложение жестоко рухнуло с этим сообщением об ошибке:

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.

Пожалуйста, помогите мне

Редактировать:

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();
}

Так... 1 поток с одной вещью для загрузки == хорошо. 2 объекта в двух потоках также являются окей. Когда я пытаюсь загрузить 3 или более объектов в два потока, все это идет в ад.

Кроме того,это может иметь какое-то отношение к тому, чтоИнформация о файлах одновременно с изменением?

РЕДАКТИРОВАТЬ: По какой-то причине мое приложение теперь работает в 4.8.0, когда я компилирую его в Visual Studio.Может ли это быть как-то связано с версией 4.7.4?

Ответы [ 3 ]

3 голосов
/ 27 февраля 2012

QThread::start() - это то, что фактически запускает thread (как другое thread).QThread::run() - это обычная функция, поэтому, если вы вызываете ее без вызова start(), вы выполняете ее в главном потоке.

Самое смешное, что ваш производный класс был создан восновной поток, поэтому он «принадлежит» в основном потоке.Я предполагаю, что вы отдаете свой класс как родитель чему-то другому;это то, что генерирует сообщение об ошибке, когда вы пытаетесь сделать это после вызова start().Можете ли вы оставить эти объекты родительскими?

Вы не сможете создавать объекты графического интерфейса в другом потоке, Qt просто не позволяет этого (пока?).Но другие объекты могут быть созданы, вы просто не можете дать им родителей в другом потоке.

2 голосов
/ 14 июня 2012

Ошибка, которую вы получаете

ASSERT failure in QMutexLocker: "QMutex pointer is misaligned"

, возникает при создании объекта QMutexLocker, если переданный в конструктор объект QMutex не выровнен на 2-байтовой границе в ОЗУ (в Qt 4.7.1 внаименее).

Объект QMutexLocker использует одну переменную-член для представления местоположения мьютекса в памяти и его состояния (независимо от того, заблокирован он или нет).Состояние представлено младшим значащим битом переменной, в то время как младший значащий бит указателя мьютекса предполагается равным нулю.Если этот бит не равен нулю, генерируется исключение ASSERT, приведенное выше.

Единственная причина смещения указателя QMutex, о которой я могу думать, это утечка памяти или повреждение.Проверьте, уничтожаете ли вы всю память, которую вы выделяете (особенно в циклах), и проверяете все типы типов (вы, возможно, навели указатель меньшего типа на указатель большего типа, повреждающий память) и строки с нулевым символом в конце (которые повредят памятьесли не правильно прекращено).Наконец, проверьте безопасность потоков всей памяти, которую вы разделяете между потоками.

1 голос
/ 27 февраля 2012

Вероятно, это связано с тем, что создан какой-то объект в вашем приложении.Я бы порекомендовал наследовать от QObject вместо QThread и переместить объект в рабочий поток (QObject :: moveToThread ()).Также переместите любой код, который инициализирован в FileUploader, в отдельный слот в FileUploader, скажем, init ().Вызывать init (QMetaObject :: invokeMethod ()), когда поток работает (после вызова start в рабочем потоке).Например:

FileUploader : public QOject
{
...
public slots:
void init() { Foo *foo = new Foo(this); }
}

// main thread
QThread worker;
FileUploader *fup = new FileUploader();
fup->moveToThread(&worker);
worker.start();
QMetaObject::invokeMethod(fup, "init", Qt::QueuedConnection); 
//
...