【问题标题】:QSignalSpy can not be used with threadsQSignalSpy 不能与线程一起使用
【发布时间】:2015-02-04 20:00:48
【问题描述】:

我写了一个执行工作对象的线程。一切正常。生成的信号也会按应有的方式发出。当然,我处理了有关线程/对象关联性的常见错误。

今天我为这些工作线程/线程编写了一个自动化模块测试。我创建了一个 QSignalSpy 来等待工作对象(已移动到线程)发出的信号,如下所示:

QSignalSpy spy(worker, SIGNAL(Success()));
thread.ExecuteWorker();
QVERIFY(spy.wait()); // Error in this line

我在标记的行中遇到了一个众所周知的错误:

QObject::killTimer: timers cannot be stopped from another thread

首先,我预计会出现错误,因为 wait() 中的某些代码在错误的线程中执行。然后在QSignalSpy的实现中发现了如下代码:

if (!QMetaObject::connect(obj, sigIndex, this, memberOffset, Qt::DirectConnection, 0))
{
   qWarning("QSignalSpy: QMetaObject::connect returned false. Unable to connect.");
   return;
}

这显然意味着 QSignalSpy 一直使用 DirectConnection 并且不能用于监视生活在不同线程中的对象的信号。

他们为什么在 Qt5.3 中这样编程?这是一个错误还是预期的行为?我该如何解决这个限制?

【问题讨论】:

    标签: c++ qt qthread qtestlib qsignalspy


    【解决方案1】:

    不幸的是,这是一个长期存在的问题,公平地说已经超过六年了:

    QSignalSpy crashes if signal is emitted from worker thread

    几年前,我在 Qt 贡献者峰会上遇到了 Jason,但在那之后不久,他就离开了诺基亚,因为诺基亚关闭了他工作的布里斯班办事处。遗憾的是,在那之后,Qt 的这个测试模块并没有多少贡献。

    最近在邮件列表上也有更多关于它的讨论:

    Why is QSignalSpy using Qt::DirectConnection?

    Roland 提出的解决方案是维护者 Thiago 也接受的:

    if (thread() != QThread::currentThread())
    {
        QMetaObject::invokeMethod(this, "exitLoop", Qt::QueuedConnection);
        return;
    }
    

    在 5.4 之前没有进入这确实有点遗憾。话虽如此,随着更改的合并,这将在 Qt 5.4 中得到修复:

    Make QTestEventLoop::exitLoop() thread-safe

    【讨论】:

    • 我阅读了链接。尤其是有趣的 ML 讨论。我不确定我是否完全理解他们决定在 QTestEventLoop 端解决问题的原因,而不是在 QSignalSpy 连接端。两种解决方案都可以解决问题,不是吗?
    • 因为维护者喜欢这种方式。
    【解决方案2】:

    为了使 QSignalSpy 跨线程可靠地工作,我使用以下方法:我将 spy 移动到工作线程并重新实现等待函数,如下所示:

    #include <QSignalSpy>
    #include <QTime>
    struct ThreadsafeQSignalSpy : QSignalSpy
    {
        template <typename Func>
        ThreadsafeQSignalSpy(const typename QtPrivate::FunctionPointer<Func>::Object *obj, Func signal0)
            : QSignalSpy(obj, signal0)
        {}
    
        bool wait(int timeout)
        {
            auto origCount(count());
            QTime timer;
            timer.start();
    
            while (count() <= origCount && timer.elapsed() < timeout)
                QCoreApplication::instance()->processEvents(QEventLoop::AllEvents, timeout/10);
            return count() > origCount;
        }
    };
    
    
    void TestSuite::testFunction()
    {
        QThread thread;
        ...
        ThreadsafeQSignalSpy spy;
        spy.moveToThread(thread);
        /// now wait should work
        ...
        QVERIFY(spy.wait(1000));
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-09-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多