Я написал поток, который выполняет рабочий объект. Все отлично работает Также результирующие сигналы излучаются так, как должны. Конечно, я позаботился об обычных ошибках в отношении схожести потоков / объектов.
Сегодня я написал автоматизированный модульный тест для этих рабочих / потоков. Я создал 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? Это ошибка или намеренное поведение? Как я могу обойти это ограничение?
К сожалению, это давняя проблема, более шести лет, чтобы быть справедливой:
QSignalSpy падает, если сигнал испускается из рабочего потока
Я встретил Джейсона на саммите Qt Contributor пару лет назад, но вскоре после этого он покинул Nokia, когда Nokia закрыла офис в Брисбене, где он работал. После этого, к сожалению, в этом тестовом модуле Qt не было особого вклада.
Недавно в списке рассылки тоже было больше дискуссий:
Почему QSignalSpy использует Qt :: DirectConnection?
Решение, предложенное Роландом, заключалось в том, что сопровождающий, Тиаго, также принял:
if (thread() != QThread::currentThread())
{
QMetaObject::invokeMethod(this, "exitLoop", Qt::QueuedConnection);
return;
}
Немного стыдно, что это не до 5.4. Сказав это, это будет исправлено в Qt 5.4, поскольку изменение было объединено:
Для обеспечения надежной работы QSignalSpy между потоками я использую следующий подход: я перемещаю шпиона в рабочий поток и заново реализую функцию ожидания следующим образом:
#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));
}