2

新しいスレッドを作成するアップローダーを作成しようとしていますが、すべてのスレッドに 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) を作成し、それにリクエストを置くオブジェクト) を作成して、コンストラクターで何も作成されないようにします (これにより、代わりにメインスレッドで終了しますか?)。run 関数は、QNetworkAccessManager に与えられる要求を作成し、それが "finished(QNetworkReply)" というシグナルを受け取るまで何もせず、次のものを取得します (リストがステップスルーされるまで続きます)。

次に、メイン アプリケーションで 2 つの新しいスレッドを作成し、run() を使用してそれらを開始すると、ID が両方のスレッドで同じであることを除いて機能します。代わりに「start()」を呼び出すと、次のようにクラッシュします: QObject: Cannot create children for a parent that is in a different thread. (親は FileUploader(0x2580748)、親のスレッドは QThread(0x4fb2b8)、現在のスレッドは FileUploader(0x2580748)

しかし!リストの手順を開始する直前に、threadId を出力すると、それらは同じではなくなります。

私は何を間違っていますか、またはこれを行う必要があります: http://labs.qt.nokia.com/2006/12/04/threading-without-the-headache/ ?

編集:

私はそれを変更し、名前を run から start に変更し、このラッパーを作成しました (そして、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);

1 つのオブジェクトをアップロードすると、1 つのスレッドしか使用しないため、機能します。しかし、分割したオブジェクトがさらにある場合、アプリケーションは次のエラー メッセージで激しくクラッシュしました。

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つある1つのスレッド== OK。2 つのスレッド上の 2 つのオブジェクトも問題ありません。2 つのスレッドに 3 つ以上のオブジェクトをアップロードしようとすると、すべてがうまくいきません。

また、変更と同時にUIがファイルの情報を読み取っていることと関係があるのでしょうか?

編集: 何らかの理由で、ビジュアル スタジオでコンパイルすると、アプリが 4.8.0 で動作するようになりました。バージョン 4.7.4 と何か関係があるのでしょうか?

4

3 に答える 3

4

QThread::start()実際にthread(別の としてthread) を開始するものです。 QThread::run()は単なる通常の関数であるため、最初に呼び出さずに呼び出すとstart()、メイン スレッドで実行されます。

面白いことに、派生クラスはメイン スレッドで作成されたので、メイン スレッドに「所属」します。クラスを親として別のものに与えていると思います。を呼び出した後にこれを実行しようとすると、エラー メッセージが生成されますstart()。それらのオブジェクトを親なしのままにしておくことはできますか?

別のスレッドで gui オブジェクトを作成することはできません。Qt では許可されていません (まだ?)。ただし、他のオブジェクトを作成することはできますが、別のスレッドでそれらに親を与えることはできません。

于 2012-02-27T00:41:35.667 に答える
3

あなたが受け取っているエラー

ASSERT failure in QMutexLocker: "QMutex pointer is misaligned"

コンストラクターに渡された QMutex オブジェクトが RAM の 2 バイト境界に配置されていない場合 (少なくとも Qt 4.7.1 では)、QMutexLocker オブジェクトの作成時に発生します。

QMutexLocker オブジェクトは、1 つのメンバー変数を使用して、メモリ内のミューテックスの場所とその状態 (ロックされているかどうか) の両方を表します。状態は変数の最下位ビットで表されますが、mutex ポインターの最下位ビットはゼロと見なされます。このビットがゼロでない場合、上記の ASSERT 例外がスローされます。

私が考えることができる QMutex ポインタのミスアライメントの唯一の理由は、メモリ リークまたは破損です。割り当てたすべてのメモリを破棄するかどうか (特にループ内) を確認し、すべての型キャスト (小さな型のポインタを大きな型のポインタにキャストしてメモリを損傷した可能性があります) と null で終わる文字列 (メモリを損傷する可能性があります) をチェックします。適切に終了しない場合)。最後に、スレッド間で共有するすべてのメモリのスレッド セーフを確認します。

于 2012-06-14T10:21:32.763 に答える
0

これはおそらく、アプリケーション内のいくつかのオブジェクトが作成される順序によるものです。QThread ではなく QObject から継承し、代わりにオブジェクトをワーカー スレッド (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); 
//
于 2012-02-27T01:42:25.470 に答える