如何在Qt中发出跨线程信号?

时间:2009-03-12 11:38:16

标签: c++ qt signals-slots qt-signals

Qt文档指出信号和插槽可以是directqueuedauto

它还声明,如果拥有插槽的对象'生活'在与拥有信号的对象不同的线程中,则发出此类信号就像发布消息一样 - 信号发出将立即返回,并且将在目标线程的事件循环中调用slot方法。

不幸的是,文档没有指明'生命'代表并且没有可用的示例。我尝试了以下代码:

main.h:

class CThread1 : public QThread
{
Q_OBJECT
public:
    void run( void )
    {
        msleep( 200 );
        std::cout << "thread 1 started" << std::endl;
        MySignal();
        exec();
    }
signals:
    void MySignal( void );
};

class CThread2 : public QThread
{
Q_OBJECT
public:
    void run( void )
    {
        std::cout << "thread 2 started" << std::endl;
        exec();
    }
public slots:
    void MySlot( void )
    {
        std::cout << "slot called" << std::endl;
    }
};

main.cpp:

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    CThread1 oThread1;
    CThread2 oThread2;
    QObject::connect( & oThread1, SIGNAL( MySignal() ),
        & oThread2, SLOT( MySlot() ) );
    oThread1.start();
    oThread2.start();
    oThread1.wait();
    oThread2.wait();
    return a.exec();
}

输出是:

thread 2 started
thread 1 started
永远不会打电话给{p> MySlot() :(。我做错了什么?

3 个答案:

答案 0 :(得分:46)

您的代码存在很多问题:

  • 像Evan所说的那样,缺少emit关键字
  • 所有对象都存在于主线程中,只有run方法中的代码存在于其他线程中,这意味着MySlot槽将在主线程中被调用,而我不确定这是你想要的什么
  • 你的插槽永远不会被调用,因为主事件循环永远不会被启动:你的两次调用wait()只会在很长一段时间后超时(你可能会在发生这种情况之前杀死你的应用程序)并且我不会不管怎么说,这就是你想要的,无论如何它们在你的代码中都没用。

这段代码最有可能工作(虽然我没有测试过),我认为它可以做你想做的事情:

class MyObject : public QObject
{
    Q_OBJECT
public slots:
    void MySlot( void )
    {
        std::cout << "slot called" << std::endl;
    }
};

class CThread1 : public QThread
{
    Q_OBJECT
public:
    void run( void )
    {
        std::cout << "thread 1 started" << std::endl;
        int i = 0;
        while(1)
        {
           msleep( 200 );
           i++;
           if(i==1000)
              emit MySignal();
        }
    }
signals:
    void MySignal( void );
};

class CThread2 : public QThread
{
    Q_OBJECT
public:
    void run( void )
    {
        std::cout << "thread 2 started" << std::endl;
        exec();
    }
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    CThread1 oThread1;
    CThread2 oThread2;
    MyObject myObject;
    QObject::connect( & oThread1, SIGNAL( MySignal() ),
        & myObject, SLOT( MySlot() ) );
    oThread2.start();
    myObject.moveToThread(&oThread2)
    oThread1.start();
    return a.exec();
}

现在MyObject将存在于thread2中(感谢moveToThread)。

MySignal应该从thread1发送(我以为我不确定那个,它可能是从主线程发送的,它并不重要)。

thread1中不需要事件循环,因为发出信号不需要事件循环。 thread2中需要一个事件循环(由exec()加载)以接收信号。

将在thread2中调用MySlot。

答案 1 :(得分:36)

不要为Qt 4.4 +

继承QThread

虽然Aiua的答案很好,但我想指出QThread和Qt 4.6或4.7的一些问题。

本文总结了这些内容:http://blog.qt.io/blog/2010/06/17/youre-doing-it-wrong/

缺乏Qt部分的文档

不幸的是,问题源于缺乏文档更新。在Qt 4.4之前,QThread没有默认的run()实现,这意味着你必须继承QThread才能使用它。

如果您使用的是Qt 4.6或4.7,那么您几乎肯定是QThread的子类。

使用moveToThread

在工作线程中获取插槽的关键是使用aiua指出的moveToThread方法。

答案 2 :(得分:1)

您应该发出启动

之类的线程函数的信号
emit operateCut(examId,examName_examTemplate[examName].studentIdRec,examName_examTemplate[examName].choiceRecA,examName_examTemplate[examName].choiceRecB,examName_examTemplate[examName].objectRecA,examName_examTemplate[examName].objectRecB);

您可以在此信号中添加多个参数