我编写了一个执行worker对象的线程。一切正常。产生的信号也会按原样发出。当然,我处理了有关线程/对象亲和力的常见错误。
今天我为那些工人/线程编写了一个自动模块测试。我创建了一个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中以这种方式编程?这是错误还是预期的行为?我该如何解决这个限制?
答案 0 :(得分:3)
遗憾的是,这是一个长期存在的问题,超过六年是公平的:
QSignalSpy crashes if signal is emitted from worker thread
几年前我在Qt贡献者峰会上遇到了杰森,但随后诺基亚关闭了布里斯班办公室,他离开了诺基亚。在那之后,遗憾的是,在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中得到修复:
答案 1 :(得分:1)
为了使QSignalSpy跨线程可靠地工作,我使用以下方法:我将间谍移动到工作线程,然后重新实现wait函数,如下所示:
#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));
}