Qt阻止了事件循环

Won*_*ger 1 c++ qt multithreading qthread

我最近开始使用QT框架.昨天我开始编写一个简单的多线程应用程序.目前我有点坚持以下问题.

考虑两个工作类,它们都使用线程来进行一些"繁重的计算".第一个类FooWorker如下所示:

class FooWorker : public QObject
{
    Q_OBJECT

public:
    FooWorker() : QObject() { }
    ~FooWorker() { }

signals:
    void notify(int);
    void aborted();

public slots:
    void doWork()
    {
        int counter = 0;
        forever {

            // For the sake of this example this reassembles a heavy computational process
            if(counter++ < 10) {
                emit notify(counter);
                QThread::sleep(1);
            } else {
                counter = 0;

                // Wait until we get a signal to restart the process
                mutex_.lock();
                condition_.wait(&mutex_);
                mutex_.unlock();
            }
            // We should check for a cancellation flag every iteration...
        }

        emit aborted();
    }

private:
    QMutex mutex_;
    QWaitCondition condition_;
};
Run Code Online (Sandbox Code Playgroud)

插槽'doWork'将被安排在另一个线程中运行.插槽将永远运行,并且每秒发出一个信号,直到发出10个通知.之后我们等到它再次被唤醒.

第二类BarWorker看起来像这样:

class BarWorker : public QObject
{
    Q_OBJECT

public:
    BarWorker() : QObject() { }
    ~BarWorker() { }

signals:
    void aborted();

public slots:
    void doWork()
    {
        forever {
            // Another heavy computational process
            QThread::sleep(1);

            // We should check for a cancellation flag every iteration...
        }

        emit aborted();
    }

    void onNotify(int value)
    {
        qDebug() << "Notification value:" << value;
    }
};
Run Code Online (Sandbox Code Playgroud)

插槽'doWork'将再次安排在另一个线程中运行.该插槽将永远运行以执行繁重的计算过程.一旦完成该过程,我们将一直等到它再次被唤醒(为了这个例子,我把它留在了这个类中).

最后主要看起来如下:

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QThread* barThread = new QThread();
    BarWorker* barWorker = new BarWorker();
    barWorker->moveToThread(barThread);

    QThread* fooThread = new QThread();
    FooWorker* fooWorker = new FooWorker();
    fooWorker->moveToThread(fooThread);

    // Automatically deletes worker and thread
    QObject::connect(fooThread, SIGNAL(started()), fooWorker, SLOT(doWork()));
    QObject::connect(fooWorker, SIGNAL(aborted()), fooThread, SLOT(quit()));
    QObject::connect(fooWorker, SIGNAL(aborted()), fooWorker, SLOT(deleteLater()));
    QObject::connect(fooThread, SIGNAL(finished()), fooThread, SLOT(deleteLater()));

    QObject::connect(barThread, SIGNAL(started()), barWorker, SLOT(doWork()));
    QObject::connect(barWorker, SIGNAL(aborted()), barThread, SLOT(quit()));
    QObject::connect(barWorker, SIGNAL(aborted()), barWorker, SLOT(deleteLater()));
    QObject::connect(barThread, SIGNAL(finished()), barThread, SLOT(deleteLater()));

    QObject::connect(fooWorker, SIGNAL(notify(int)), barWorker, SLOT(onNotify(int)), Qt::QueuedConnection);

    fooThread->start();
    barThread->start();

    return a.exec();
}
Run Code Online (Sandbox Code Playgroud)

当我运行应用程序时,没有任何打印.这是预期的,因为BarWorker实例的事件循环被阻止.当'notify'信号被发出时,'onNotify'槽被排队到事件队列中.因为我们在'doWork'插槽中有一个永无止境的循环(直到我们手动中止它),所以不会调用'onNotify'插槽.为了解决这个问题,我可以做一些事情,即:

  1. 使用Qt :: DirectConnection标志将'notify'信号连接到'onNotify'插槽.这种方式看起来像是在信令线程上执行的普通函数调用.
  2. 偶尔调用QCoreApplication :: processEvents()方法来强制处理事件队列.
  3. 我不知道此时的未知解决方案:)???

我希望有人有一些替代方案可以解决这个问题,甚至建议采用一种完全不同的方法,因为恕我直言上面的解决方案有点难看,感觉不对.

Jer*_*ner 5

我不认为这里有任何"神奇"的解决方案; 如果一个线程正在运行你自己的自定义事件循环,它就无法运行Qt的事件循环.在实践中,有两种常见的解决方案,它们实际上是同一枚硬币的两面:

  1. 如您在问题中所建议的那样,定期从事件循环中调用processEvents(),以便Qt事件处理代码偶尔可以运行并处理传入的异步信号.

  2. 在doWork()方法中没有长时间运行的循环.相反,做一些简单的工作,将工作的结果/状态存储在成员变量或某个地方,然后调用类似QTimer :: singleShot(0,this,SLOT(doWork())的东西,以便Qt事件第一次调用doWork()后,循环将再次调用doWork()方法.这样,Qt事件循环永远不会被持续超过单个doWork()调用占用的(短暂)时间段.

在这两个选项中,我认为第二个选项更可取,因为它允许Qt事件循环以其正常方式运行,并且它还避免了潜在的跳过自己的鞋带问题 - 例如想象如果在使用解决方案时(1)调用processEvents()会调用一个删除BarWorker对象的槽.当processEvents()调用返回时,BarWorker :: doWork()将继续执行,但此时,它可能作为其正常执行的一部分访问的所有本地成员变量和虚拟方法都已被销毁,并且读取或写入它们将导致未定义的行为(如果你很幸运,一个易于调试的崩溃).使用solution(2)时可能不会发生这种情况,因为如果在调用doWork()之间删除BarWorker对象,则会安全地取消对doWork()的任何排队异步调用.