C++ Qt 5:从非Qt线程发出信号

C++ Qt 5:从非Qt线程发出信号,c++,qt,qt5,C++,Qt,Qt5,我试图实现的是在Qt-qtcserver/socket之上构建的跨平台TCP套接字库。我遇到了一个问题,即没有Qt事件循环的非Qt线程发出的信号,不会被带有事件循环的QThread中的对象接收 我发现,根据和问题,从非Qt线程发出的消息在使用Qt::QueuedConnection连接类型显式设置之前是有效的。这些问题相当古老,与Qt4有关。所以我想知道Qt5是否仍然支持这个功能 我研究了Qt 5源代码,发现: 发出一个信号只不过是对 如果连接类型设置为Qt::QueuedConnection,

我试图实现的是在Qt-qtcserver/socket之上构建的跨平台TCP套接字库。我遇到了一个问题,即没有Qt事件循环的非Qt线程发出的信号,不会被带有事件循环的QThread中的对象接收

我发现,根据和问题,从非Qt线程发出的消息在使用Qt::QueuedConnection连接类型显式设置之前是有效的。这些问题相当古老,与Qt4有关。所以我想知道Qt5是否仍然支持这个功能

我研究了Qt 5源代码,发现:

  • 发出一个信号只不过是对
  • 如果连接类型设置为Qt::QueuedConnection,或者当前线程(发射器线程)与线程接收器所在的线程不同(在我的示例中,Qt::QueuedConnection是显式设置的),则依次调用
  • 创建事件对象并调用
  • 执行正确的锁定,并将事件放入接收器事件队列。尽管postEvent是一个静态QCoreApplication函数,它使用self-指向当前静态QCoreApplication singleton的指针,但即使没有全局QCoreApplication对象(即self==0),它也应该正常工作
  • 考虑到这一点,我假设为了使signal&slot机制正常工作,只有接收方线程必须具有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线程中,并连接到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
    
    但如果我保持原样,我只会

    mySignal emitted
    QEventLoop: Cannot be used without QApplication
    

    那么,信号和插槽机制在这种情况下不工作的原因是什么?如何使其工作?

    错误消息准确地告诉您需要知道的内容:如果没有
    QCoreApplication
    存在,您无法使用事件循环系统。这就是全部。你对Qt内部的所有探索都是有教育意义的,但都是一种误导。如果这很重要的话就没有了

    只有接收方线程必须具有将从队列中调度事件的Qt事件循环

    没错

    这是否意味着如果我在QThread内创建QCoreApplication,这个系统应该可以工作

    您可以在任何线程上创建它(与只能在主线程上运行的QGUI应用程序不同)。但请确保与Qt静态链接。否则,如果您使用系统Qt进行链接,那么如果您创建应用程序的第二个实例,您将变得与使用相同Qt的任何进程二进制不兼容。因此,如果您使用SystemQT,您可以通过检查应用程序实例是否存在来解决问题,并且只在应用程序实例不存在时创建一个应用程序实例

    此外,您不应该真的需要在自定义线程中创建应用程序实例。您的库应该接受应该在调用进程的主线程中执行的初始化调用。如果应用程序对象不存在,此初始化可以创建应用程序对象

    // https://github.com/KubaO/stackoverflown/tree/master/questions/twothreads-41044526
    #include <QtCore>
    
    // see http://stackoverflow.com/questions/40382820
    template <typename Fun> void safe(QObject * obj, Fun && fun) {
        Q_ASSERT(obj->thread() || qApp && qApp->thread() == QThread::currentThread());
        if (Q_LIKELY(obj->thread() == QThread::currentThread()))
            return fun();
        struct Event : public QEvent {
          using F = typename std::decay<Fun>::type;
          F fun;
          Event(F && fun) : QEvent(QEvent::None), fun(std::move(fun)) {}
          Event(const F & fun) : QEvent(QEvent::None), fun(fun) {}
          ~Event() { fun(); }
        };
        QCoreApplication::postEvent(
              obj->thread() ? obj : qApp, new Event(std::forward<Fun>(fun)));
    }
    
    class Worker : public QObject {
       Q_OBJECT
       QBasicTimer m_timer;
       int n = 0;
       void timerEvent(QTimerEvent *event) override {
          if (event->timerId() == m_timer.timerId())
             emit hasData(n++);
       }
    public:
       Q_SIGNAL void hasData(int);
       Q_SLOT void onData(int d) { qDebug() << QThread::currentThread() << "got data" << d; }
       void start() {
          safe(this, [this]{ m_timer.start(50,this); });
       }
       void quit() {
          safe(this, [this]{ m_timer.stop(); thread()->quit(); });
       }
    };
    
    class Library {
       QByteArray dummy{"dummy"};
       int argc = 1;
       char *argv[2] = {dummy.data(), nullptr};
       QScopedPointer<QCoreApplication> app;
       static Library *m_self;
       struct {
          Worker worker;
          QThread thread;
       } m_jobs[3];
    public:
       Library() {
          Q_ASSERT(!instance());
          m_self = this;
          if (!qApp) app.reset(new QCoreApplication(argc, argv));
    
          for (auto & job : m_jobs) {
             job.worker.moveToThread(&job.thread);
             job.thread.start();
             job.worker.start();
             QObject::connect(&job.worker, &Worker::hasData, &m_jobs[0].worker, &Worker::onData);
          }
       }
       ~Library() {
          for (auto &job : m_jobs) {
             job.worker.quit();
             job.thread.wait();
          }
       }
       static Library *instance() { return m_self; }
    };
    Library *Library::m_self;
    
    // API
    void initLib() {
       new Library;
    }
    
    void finishLib() {
       delete Library::instance();
    }
    
    int main()
    {
       initLib();
       QThread::sleep(3);
       finishLib();
    }
    
    #include "main.moc"
    
    //https://github.com/KubaO/stackoverflown/tree/master/questions/twothreads-41044526
    #包括
    //看http://stackoverflow.com/questions/40382820
    模板无效安全(QObject*obj,乐趣和乐趣){
    Q|u断言(obj->thread()| | qApp&&qApp->thread()==QThread::currentThread());
    如果(很可能是(obj->thread()==QThread::currentThread())
    返回乐趣();
    结构事件:公共QEvent{
    使用F=typename std::decay::type;
    F乐趣;
    事件(F&&fun):QEvent(QEvent::None),fun(std::move(fun)){}
    事件(const F&fun):QEvent(QEvent::None),fun(fun){
    ~Event(){fun();}
    };
    QCoreApplication::postEvent(
    obj->thread()?obj:qApp,新事件(std::forward(fun));
    }
    班级工作人员:公共QObject{
    Q_对象
    基本定时器;
    int n=0;
    void timerEvent(QTimerEvent*事件)覆盖{
    如果(事件->timerId()==m_timer.timerId())
    发射hasData(n++);
    }
    公众:
    Q_信号无效数据(int);
    
    Q_SLOT void onData(int d){qDebug()QCoreApplication对象所在的位置是否重要(就线程关联而言)?这是否意味着如果我在QThread内创建QCoreApplication,该系统应该可以工作?是的,它应该可以工作,即使您不需要在线程内创建
    QCoreApplication
    ,因为Qt的事件循环利用本机事件循环,所以如果您将DLL注入应用程序,您只需要应用程序的实例,但只要底层应用程序启动本机事件循环,就不需要调用
    exec
    #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();
    }
    
    mySignal emitted
    mySlot called! It works!
    
    mySignal emitted
    QEventLoop: Cannot be used without QApplication
    
    // https://github.com/KubaO/stackoverflown/tree/master/questions/twothreads-41044526
    #include <QtCore>
    
    // see http://stackoverflow.com/questions/40382820
    template <typename Fun> void safe(QObject * obj, Fun && fun) {
        Q_ASSERT(obj->thread() || qApp && qApp->thread() == QThread::currentThread());
        if (Q_LIKELY(obj->thread() == QThread::currentThread()))
            return fun();
        struct Event : public QEvent {
          using F = typename std::decay<Fun>::type;
          F fun;
          Event(F && fun) : QEvent(QEvent::None), fun(std::move(fun)) {}
          Event(const F & fun) : QEvent(QEvent::None), fun(fun) {}
          ~Event() { fun(); }
        };
        QCoreApplication::postEvent(
              obj->thread() ? obj : qApp, new Event(std::forward<Fun>(fun)));
    }
    
    class Worker : public QObject {
       Q_OBJECT
       QBasicTimer m_timer;
       int n = 0;
       void timerEvent(QTimerEvent *event) override {
          if (event->timerId() == m_timer.timerId())
             emit hasData(n++);
       }
    public:
       Q_SIGNAL void hasData(int);
       Q_SLOT void onData(int d) { qDebug() << QThread::currentThread() << "got data" << d; }
       void start() {
          safe(this, [this]{ m_timer.start(50,this); });
       }
       void quit() {
          safe(this, [this]{ m_timer.stop(); thread()->quit(); });
       }
    };
    
    class Library {
       QByteArray dummy{"dummy"};
       int argc = 1;
       char *argv[2] = {dummy.data(), nullptr};
       QScopedPointer<QCoreApplication> app;
       static Library *m_self;
       struct {
          Worker worker;
          QThread thread;
       } m_jobs[3];
    public:
       Library() {
          Q_ASSERT(!instance());
          m_self = this;
          if (!qApp) app.reset(new QCoreApplication(argc, argv));
    
          for (auto & job : m_jobs) {
             job.worker.moveToThread(&job.thread);
             job.thread.start();
             job.worker.start();
             QObject::connect(&job.worker, &Worker::hasData, &m_jobs[0].worker, &Worker::onData);
          }
       }
       ~Library() {
          for (auto &job : m_jobs) {
             job.worker.quit();
             job.thread.wait();
          }
       }
       static Library *instance() { return m_self; }
    };
    Library *Library::m_self;
    
    // API
    void initLib() {
       new Library;
    }
    
    void finishLib() {
       delete Library::instance();
    }
    
    int main()
    {
       initLib();
       QThread::sleep(3);
       finishLib();
    }
    
    #include "main.moc"