60

Qt 文档指出信号和槽可以是direct,queuedauto.

它还指出,如果拥有插槽的对象“生活”在与拥有信号的对象不同的线程中,则发出这样的信号就像发布消息一样——信号发出将立即返回,并且插槽方法将在目标线程的事件循环中被调用。

不幸的是,文档没有具体说明“生活”代表什么,也没有可用的示例。我尝试了以下代码:

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

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

4

3 回答 3

48

您的代码存在很多问题:

  • 就像 Evan 所说的,缺少 emit 关键字
  • 您所有的对象都存在于主线程中,只有运行方法中的代码存在于其他线程中,这意味着 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() 启动)来接收信号。

MySlot 将在 thread2 中调用。

于 2009-03-12T13:49:39.707 回答
39

不要为 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 方法。

于 2011-12-02T17:24:34.250 回答
0

你应该发出信号来启动你的线程函数,比如

emit operateCut(examId,examName_examTemplate[examName].studentIdRec,examName_examTemplate[examName].choiceRecA,examName_examTemplate[examName].choiceRecB,examName_examTemplate[examName].objectRecA,examName_examTemplate[examName].objectRecB);

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

于 2018-12-22T03:27:49.597 回答