首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Qt 5:从非Qt线程发出信号。

Qt 5:从非Qt线程发出信号。
EN

Stack Overflow用户
提问于 2016-12-08 16:42:27
回答 1查看 3.2K关注 0票数 2

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

根据的问题,我发现从非Qt线程发出的消息以前使用了Qt::QueuedConnection连接类型集。这些问题相当古老,与Qt 4有关。因此,我想知道Qt 5中是否仍然支持这种功能。

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

  1. 发出信号只是对QMetaObject::激活的调用
  2. 反过来,如果将连接类型设置为Qt::QueuedConnection,或者当前线程(发射器线程)与线程接收器所在的线程不同(在我的示例中,Qt::QueuedConnection被显式设置),则QMetaObject::激活将调用激活
  3. 激活创建一个事件对象并调用QCoreApplication::postEvent
  4. QCoreApplication::postEvent进行适当的锁定,并将事件放入接收方事件队列。尽管postEvent是一个静态QCoreApplication函数,它使用的是指向当前静态QCoreApplication单例的指针,即使没有全局QCoreApplication对象(即self == 0),它也应该正常工作。

因此,我假设要使信号和插槽机制正常工作,只有接收线程必须有Qt事件循环,该循环将从队列中分派事件,如果我错了,请纠正我。

尽管如此,从非Qt线程发出信号对我来说并不有效。我已经创建了尽可能简单的演示应用程序来演示信号和插槽的故障。

MyThread组件只是继承QThread并在其内部移动(moveToThread)、QObject派生的ThreadWorker。

MyThread.h:

代码语言:javascript
复制
#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:

代码语言:javascript
复制
#include "MyThread.h"

#include "ThreadWorker.h"

MyThread::MyThread()
    : m_worker(*this)
{
    m_worker.moveToThread(this);
}

线程工作人员需要在MyThread线程中生活,并连接到MyThread的mySignal()信号。

线程工作者:

代码语言:javascript
复制
#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:

代码语言:javascript
复制
#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:

代码语言:javascript
复制
#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创建的注释,就会得到正确的输出:

代码语言:javascript
复制
mySignal emitted
mySlot called! It works!

但如果我保持原样,我只会得到

代码语言:javascript
复制
mySignal emitted
QEventLoop: Cannot be used without QApplication

那么,在这种情况下,信号和插槽机制不起作用的原因是什么?如何让它发挥作用?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2016-12-08 16:48:32

错误消息会告诉确切地告诉您需要知道什么:如果没有QCoreApplication,您就不能使用事件循环系统。就这样。你对Qt内部的所有探索都是有教育意义的,但却是一只红鲱鱼。一点也不重要。

只有接收方线程必须具有Qt事件循环,该循环将从队列中分派事件。

是这样的。

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

您可以在任何线程上创建它(与只能驻留在主线程上的QGuiApplication相反)。但是,请确保静态地链接到Qt。否则,如果您要链接到system,如果您创建了应用程序的第二个实例,那么您将变成与使用同一个Qt的任何进程不兼容的二进制文件。因此,如果您使用system,您可以检查应用程序实例是否存在,并且只在尚不存在的情况下创建一个实例。

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

代码语言:javascript
复制
// 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"
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/41044526

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档