QT 5:从非QT线程发射信号



我要实现的是建立在qt qtcpserver/套接字顶部的跨平台TCP套接字库。我面临的问题是,QThread中的对象未收到来自没有QT事件循环的非QT线程的信号,并没有收到事件循环。

我发现,根据QT :: QueuedConnection连接类型的发出从非QT线程发出,并根据此问题明确设置。这些问题很旧,与QT 4有关。因此,我想知道是否仍支持QT 5中的此功能。

我探索了QT 5源代码,并找到了:

  1. 发出信号只是对qmetaObject ::激活
  2. 的调用
  3. qmetaObject ::激活,反过来,呼叫queued_activate,如果将连接类型设置为qt :: queuedConnection或当前线程(emitter thread)与线程接收器不同(在我的情况下,qt :: queuedconnection is明确设置)。
  4. queued_activate创建事件对象并调用qcoreapplication :: postevent
  5. qcoreapplication :: poestvent进行适当的锁定,并将事件放入接收器事件队列。尽管事后是一种静态QCoreApplication函数,它使用自我 - 指向当前静态QcoreApplication singleton的指针,即使没有全局qcoreapplication对象(即self == 0)。
  6. ,它也应该正常工作。

给出了这一点,我想对于信号&插槽机制才能正常工作,只有接收器线程必须具有QT事件循环,该QT将从队列中派遣事件,如果我错了,请纠正我。

尽管如此,从非QT线程发出信号对我不起作用。我创建了尽可能简单的演示应用程序,以演示信号& stlot的故障。

mythread组件仅继承qthread并移动内部(movetothread)qObject-dreadworker。

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's 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

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

错误消息告诉您恰好您需要知道的内容:如果没有QCoreApplication,就无法使用事件循环系统。就这样。您对QT内部的所有探索都是教育意义的,但是红鲱鱼。

只有接收器线程必须具有QT事件循环,该QT将从队列中派遣事件

是正确的。

这是否意味着如果我在qthread内部创建qcoreapplication,则该系统应该起作用?

您可以在任何线程上创建它(与只能在主线程上生存的qGuiaPplication相比)。但是请确保您与QT静态链接。否则,如果您与系统QT链接,则如果您创建了应用程序的第二个实例,则将使用相同QT与任何过程变得不兼容。因此,如果您使用系统QT,则可以检查是否存在应用程序实例,并且仅在不存在时创建一个。

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

// 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"

相关内容

  • 没有找到相关文章

最新更新