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

时间:2021-11-22 11:00:49

Qt documentation states that signals and slots can be direct, queued and auto.

Qt文档指出信号和插槽可以是直接的,排队的和自动的。

It also stated that if object that owns slot 'lives' in a thread different from object that owns signal, emitting such signal will be like posting message - signal emit will return instantly and slot method will be called in target thread's event loop.

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

Unfortunately, documentation do not specify that 'lives' stands for and no examples is available. I have tried the following code:

遗憾的是,文档没有指定“生命”代表并且没有可用的示例。我试过以下代码:

main.h:

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:

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();
}

Output is:

输出是:

thread 2 started
thread 1 started

MySlot() is never called :(. What I'm doing wrong?

永远不会调用MySlot():(。我做错了什么?

2 个解决方案

#1


46  

There are quite a few problems with your code :

你的代码有很多问题:

  • like said by Evan the emit keyword is missing
  • 就像Evan说的那样,emit关键字丢失了
  • all your objects live in the main thread, only the code in the run methods live in other threads, which means that the MySlot slot would be called in the main thread and I'm not sure that's what you want
  • 所有对象都存在于主线程中,只有run方法中的代码存在于其他线程中,这意味着MySlot槽将在主线程中被调用,我不确定这是你想要的
  • your slot will never be called since the main event loop will never been launched : your two calls to wait() will only timeout after a very long time (and you'll probably kill your application before that happens) and I don't think that's what you want either, anyway they really have no use in your code.
  • 你的插槽永远不会被调用,因为主事件循环永远不会被启动:你的两次wait()调用只会在很长一段时间后超时(你可能会在发生这种情况之前杀死你的应用程序)并且我不认为这也是你想要的,无论如何它们在你的代码中真的没用。

This code would most likely work (though I have not tested it) and I think it does what you want it to do :

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

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();
}

Now MyObject will live in thread2 (thanks to moveToThread).

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

MySignal should be sent from thread1 (thought I'm not sure on that one, it might be sent from main thread, it doesn't really matter).

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

No event loop is needed in thread1 since emitting a signal doesn't need an event loop. An event loop is needed in thread2 (lanched by exec()) to receive the signal.

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

MySlot will be called in thread2.

MySlot将在thread2中调用。

#2


35  

Do not subclass QThread for Qt 4.4+

While Aiua's answer is good, I want to point out some issues with QThread and Qt 4.6 or 4.7.

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

This article sums it up: http://blog.qt.io/blog/2010/06/17/youre-doing-it-wrong/

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

Lack of Documentation on Qt's part

Unfortunately the problem stems from a lack of updates to documentation. Prior to Qt 4.4 QThread had no default run() implementation, which meant that you had to subclass QThread in order to use it.

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

If you're using Qt 4.6 or 4.7 then you almost certainly should not subclass QThread.

如果您使用的是Qt 4.6或4.7,那么您几乎肯定不应该继承QThread。

Use moveToThread

The key to getting slots to execute in a worker thread is to use the moveToThread method as Aiua pointed out.

获取插槽在工作线程中执行的关键是使用moveToThread方法,如Aiua指出的那样。

#1


46  

There are quite a few problems with your code :

你的代码有很多问题:

  • like said by Evan the emit keyword is missing
  • 就像Evan说的那样,emit关键字丢失了
  • all your objects live in the main thread, only the code in the run methods live in other threads, which means that the MySlot slot would be called in the main thread and I'm not sure that's what you want
  • 所有对象都存在于主线程中,只有run方法中的代码存在于其他线程中,这意味着MySlot槽将在主线程中被调用,我不确定这是你想要的
  • your slot will never be called since the main event loop will never been launched : your two calls to wait() will only timeout after a very long time (and you'll probably kill your application before that happens) and I don't think that's what you want either, anyway they really have no use in your code.
  • 你的插槽永远不会被调用,因为主事件循环永远不会被启动:你的两次wait()调用只会在很长一段时间后超时(你可能会在发生这种情况之前杀死你的应用程序)并且我不认为这也是你想要的,无论如何它们在你的代码中真的没用。

This code would most likely work (though I have not tested it) and I think it does what you want it to do :

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

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();
}

Now MyObject will live in thread2 (thanks to moveToThread).

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

MySignal should be sent from thread1 (thought I'm not sure on that one, it might be sent from main thread, it doesn't really matter).

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

No event loop is needed in thread1 since emitting a signal doesn't need an event loop. An event loop is needed in thread2 (lanched by exec()) to receive the signal.

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

MySlot will be called in thread2.

MySlot将在thread2中调用。

#2


35  

Do not subclass QThread for Qt 4.4+

While Aiua's answer is good, I want to point out some issues with QThread and Qt 4.6 or 4.7.

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

This article sums it up: http://blog.qt.io/blog/2010/06/17/youre-doing-it-wrong/

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

Lack of Documentation on Qt's part

Unfortunately the problem stems from a lack of updates to documentation. Prior to Qt 4.4 QThread had no default run() implementation, which meant that you had to subclass QThread in order to use it.

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

If you're using Qt 4.6 or 4.7 then you almost certainly should not subclass QThread.

如果您使用的是Qt 4.6或4.7,那么您几乎肯定不应该继承QThread。

Use moveToThread

The key to getting slots to execute in a worker thread is to use the moveToThread method as Aiua pointed out.

获取插槽在工作线程中执行的关键是使用moveToThread方法,如Aiua指出的那样。