C++ QSignalSpy不能与线程一起使用

C++ QSignalSpy不能与线程一起使用,c++,qt,qthread,qtestlib,qsignalspy,C++,Qt,Qthread,Qtestlib,Qsignalspy,我编写了一个线程来执行一个worker对象。一切正常。此外,产生的信号也会按其应有的方式发出。当然,我处理了关于线程/对象关联的常见错误 今天我为这些工人/线程编写了一个自动化模块测试。我创建了一个QSignalSpy来等待worker对象(已移动到线程)发出的信号,如下所示: QSignalSpy spy(worker, SIGNAL(Success())); thread.ExecuteWorker(); QVERIFY(spy.wait()); // Error in this line

我编写了一个线程来执行一个worker对象。一切正常。此外,产生的信号也会按其应有的方式发出。当然,我处理了关于线程/对象关联的常见错误

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

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中采用这种方式编程?这是一个错误还是故意的行为?我如何才能绕过这个限制?

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

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

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

罗兰提出的解决方案是,维修人员蒂亚戈也接受了:

if (thread() != QThread::currentThread())
{
    QMetaObject::invokeMethod(this, "exitLoop", Qt::QueuedConnection);
    return;
}
这真的有点遗憾,在5.4之前没有实现。话虽如此,随着更改的合并,Qt 5.4的问题将得到解决:


为了使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));
}
#包括
#包括
结构ThreadsafeQSignalSpy:QSignalSpy
{
模板
ThreadsafeQSignalSpy(常量类型名QtPrivate::FunctionPointer::Object*obj,Func信号0)
:QSignalSpy(对象,信号0)
{}
布尔等待(整数超时)
{
自动原始计数(count());
时间计时器;
timer.start();
while(count()processEvents(QEventLoop::AllEvents,timeout/10);
返回计数()>原始计数;
}
};
void TestSuite::testFunction()
{
QThread线程;
...
ThreadsafeQSignalSpy间谍;
spy.moveToThread(线程);
///现在等待应该起作用了
...
QVERIFY(spy.wait(1000));
}

我阅读了这些链接。特别是关于ML的讨论非常有趣。我不确定我是否完全理解他们决定在QTestEventLoop端而不是QSignalSpy connect端解决问题的原因。这两种解决方案都可以解决问题,不是吗?因为维护人员喜欢这种方式。