【发布时间】:2016-12-08 16:42:27
【问题描述】:
我想要实现的是建立在 Qt QTcpServer/Socket 之上的跨平台 TCP 套接字库。我遇到了一个问题,即从没有 Qt 事件循环的非 Qt 线程发出的信号不会被具有事件循环的 QThread 中的对象接收。
根据this 和this 的问题,我发现从非Qt 线程发出之前明确设置了Qt::QueuedConnection 连接类型。这些问题相当古老,并且与 Qt 4 有关。所以我想知道 Qt 5 是否仍支持此功能。
我探索了Qt 5源代码,发现:
- 发出信号只是调用QMetaObject::activate
- QMetaObject::activate 反过来调用 queued_activate,如果连接类型设置为 Qt::QueuedConnection 或当前线程(发射线程)与线程接收者不同(在我的情况下,Qt::QueuedConnection 是显式设置的)。
- queued_activate 创建一个事件对象并调用 QCoreApplication::postEvent
- QCoreApplication::postEvent 进行适当的锁定并将事件放入 receiver 事件队列。尽管 postEvent 是一个使用 self 的静态 QCoreApplication 函数 - 一个指向当前静态 QCoreApplication 单例的指针,但即使没有全局 QCoreApplication 对象(即 self == 0),它也应该可以正常工作。
鉴于此,我认为要让信号&槽机制正常工作,只有接收器线程必须具有将从队列中分派事件的 Qt 事件循环,如果我错了,请纠正我。
尽管如此,从非 Qt 线程发出信号对我不起作用。我已经创建了尽可能简单的演示应用程序来演示信号和插槽的故障。
MyThread 组件只是继承 QThread 并在自身内部移动 (moveToThread) QObject 派生的 ThreadWorker。
MyThread.h:
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QThread>
#include "ThreadWorker.h"
class MyThread : public QThread
{
Q_OBJECT
public:
MyThread();
signals:
void mySignal();
private:
ThreadWorker m_worker;
};
#endif // MYTHREAD_H
MyThread.cpp:
#include "MyThread.h"
#include "ThreadWorker.h"
MyThread::MyThread()
: m_worker(*this)
{
m_worker.moveToThread(this);
}
线程工作者需要生活在 MyThread 线程中并连接到 MyThread 的 mySignal() 信号。
ThreadWorker.h:
#ifndef THREADWORKER_H
#define THREADWORKER_H
#include <QObject>
class MyThread;
class ThreadWorker : public QObject
{
Q_OBJECT
public:
explicit ThreadWorker(const MyThread& thread);
public slots:
void mySlot();
};
#endif // THREADWORKER_H
ThreadWorker.cpp:
#include "ThreadWorker.h"
#include <QDebug>
#include "MyThread.h"
ThreadWorker::ThreadWorker(const MyThread& thread)
: QObject(0)
{
connect(&thread, SIGNAL(mySignal()),
this, SLOT(mySlot()),
Qt::QueuedConnection);
}
void ThreadWorker::mySlot()
{
qDebug() << "mySlot called! It works!";
}
最后是main.cpp:
#include <QCoreApplication>
#include <QDebug>
#include "MyThread.h"
int main(int argc, char *argv[])
{
// QCoreApplication a(argc, argv);
MyThread my_thread;
my_thread.start();
emit my_thread.mySignal();
qDebug() << "mySignal emitted";
my_thread.wait();
// return a.exec();
}
注意,如果我取消注释 QCoreApplication 创建,我会得到正确的输出:
mySignal emitted
mySlot called! It works!
但如果我保持原样,我只会得到
mySignal emitted
QEventLoop: Cannot be used without QApplication
那么,在这种情况下,signal&slot 机制不起作用的原因是什么?如何让它发挥作用?
【问题讨论】: