如果不使用直接连接,QThread :: wait()不会返回

Kan*_*oge 5 c++ qt qthread qt5 qtcore

我在使用Qt Threads和Connections时遇到了麻烦.我找到了关于这个主题的几个教程和讨论,我按照本教程创建了线程.但是我仍然遇到了问题,在线程上调用wait()永远不会返回并且UI冻结.

之前有一个类似的问题(第二个例子): 线程之间的Qt连接类型:为什么这有效?

在该问题的最后一次编辑中,作者提到他已经造成了僵局.我假设,我在我的应用程序中也这样做.但我仍然不明白,为什么会这样.阅读建议的文章并没有帮助我理解.我只是明白了,死锁可能发生,但我不知道,是什么导致它或在我的情况下.

我还创建了一个简化为核心问题的例子.找到这个问题底部的代码.

所以我的问题是:我的例子中死锁的原因是什么?是否有解决方案而不使连接成为直接连接?

我真的很感激任何提示.

谢谢!

编辑:

由于注释我试图通过信号发送停止请求,我在线程循环中添加了一个QCoreApplication :: processEvents()调用.但主要问题仍然是一样的.

EDIT2:

在考虑了更多关于事件循环之后,我找到了一个可接受的解决方案:

thread.requestStop();

// now instead of using wait(), we poll and keep the event loop alive
// polling is not nice, but if it does not take a very long time
// for the thread to finish, it is acceptable for me.
while (thread.isRunning())
{
    // This ensures that the finished() signal
    // will be processed by the thread object
    QCoreApplication::processEvents();        
}
Run Code Online (Sandbox Code Playgroud)

这实际上有效,工人自己控制如何停止工作.

提出这个问题之后,我也对冻结问题有一个解释:调用等待似乎使主线程保持繁忙或暂停,因此它不会处理任何事件.由于线程对象存在于主线程中,因此线程的finished()信号被输入但从未被处理过.

我的隐含假设,即thread.wait()仍然会使事件循环保持工作,这显然是错误的.但是,QThread :: wait()函数对于什么有用?!?

这只是一个理论,但也许这里有人可以验证或伪造它......

编辑3(最终解决方案):

在阅读了这篇小文章并阐述了一个子类化解决方案后,我认为这对于这个特定问题更为可取.不需要事件循环,我可以直接调用不同的线程并使用互斥保护.代码更少,更易于理解,更易于调试.

我想我只会使用非子类化策略,如果与线程的交互多于开始和暂停.


我减少了例子

也许我应该指出,我不删除线程,因为在我的原始应用程序中,我想稍后恢复,所以停止它实际上意味着暂停它.

worker.h:

#ifndef WORKER_H
#define WORKER_H

#include <QObject>
#include <QMutex>

class Worker : public QObject
{
    Q_OBJECT

public:
    explicit Worker(QObject* parent = NULL);

public slots:
    void doWork();
    void requestStop();

signals:
    void finished();

private:

    bool stopRequested;
    QMutex mutex;
};

#endif // WORKER_H
Run Code Online (Sandbox Code Playgroud)

worker.cpp:

#include "worker.h"

#include <QThread>
#include <iostream>

using namespace std;

Worker::Worker(QObject *parent)
    : stopRequested(false)
{
}

void Worker::doWork()
{
    static int cnt = 0;

    // local loop control variable
    // to make the usage of the mutex easier.
    bool stopRequesteLocal = false;

    while (!stopRequesteLocal)
    {
        cout << ++cnt << endl;
        QThread::msleep(100);

        mutex.lock();
        stopRequesteLocal = stopRequested;
        mutex.unlock();
    }

    cout << "Finishing soon..." << endl;

    QThread::sleep(2);
    emit finished();
}

void Worker::requestStop()
{
    mutex.lock();
    stopRequested = true;
    mutex.unlock();
}
Run Code Online (Sandbox Code Playgroud)

主程序:

#include <QCoreApplication>
#include <QThread>
#include <QtCore>
#include <iostream>

#include "worker.h"

using namespace std;

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    QThread thread;
    Worker worker;


    QObject::connect(&thread, SIGNAL(started()), &worker, SLOT(doWork()));

    // this does not work:
    QObject::connect(&worker, SIGNAL(finished()), &thread, SLOT(quit()));

    // this would work:
    //QObject::connect(&worker, SIGNAL(finished()), &thread, SLOT(quit()), Qt::DirectConnection);

    // relocating the moveToThread call does not change anything.
    worker.moveToThread(&thread);

    thread.start();

    QThread::sleep(2);

    worker.requestStop();
    cout << "Stop requested, wait for thread." << endl;
    thread.wait();
    cout << "Thread finished" << endl;

    // I do not know if this is correct, but it does not really matter, because
    // the program never gets here.
    QCoreApplication::exit(0);
}
Run Code Online (Sandbox Code Playgroud)

Kan*_*oge 3

我将自己的答案添加到问题文本中作为编辑 3。