【问题标题】:main thread cannot get signal form worker thread主线程无法从工作线程中获取信号
【发布时间】:2015-11-15 22:41:34
【问题描述】:

这是主线的一部分。

这段代码创建了一个worker和一个workerthread。

worker被移动到workerthread。

然后工人等待信号要求它工作。

工作完成后,工作人员会发出带有结果的信号。

main 应该捕获该信号并在 main 中初始化一个变量。

main()
{.........
// This is the variable to be changed
variableToGetFromWorker = 0;

qDebug() << "Main thread: " << QThread::currentThreadId();

QThread workerThread;
worker* stupidTom = new stupidTom(number);

worker->moveToThread(&workerThread);
connect(&workerThread, &QThread::finished, &workerThread, &QObject::deleteLater);
connect(&workerThread, SIGNAL(start()), stupidTom, SLOT(doJob()));
connect(stupidTom, SIGNAL(jobDone(int)), this, SLOT(jobDone(int)));

workerThread.start();
workerThread.wait();
...........}


// This is a slot at main. Suppose to catch the signal from the worker

void main::jobDone(int result)
{
    qDebug() << "Changing variable";
    variableToGetFromWorker = result;
}

这是worker的doJob槽。

void worker::doJob()
{
    qDebug() << "worker::doJob invoked.";
    qDebug() << "worker Thread:" << QThread::currentThreadId();

    // Doing Job here

    emit jobDone(result);
}

是qDebug的结果

Main thread:  0x7ffff7fc6780
worker::doJob invoked.
worker Thread: 0x7fffdab44700

在调试模式下,我发现程序停在workerThread.wait() 永远不要去main::jobDone(int result)。是什么原因?

对主代码稍作修改:

QThread workerThread;
worker* stupidTom = new stupidTom(number);

worker->moveToThread(&workerThread);
connect(&workerThread, &QThread::finished, &workerThread, &QObject::deleteLater);
connect(&workerThread, SIGNAL(started()), stupidTom, SLOT(doJob()));
connect(stupidTom, SIGNAL(jobDone(int)), this, SLOT(jobDone(int)));
connect(stupidTom, SIGNAL(jobDone(int)), &workerThread, SLOT(quit()));

workerThread.start();
workerThread.wait();

【问题讨论】:

    标签: c++ multithreading qt


    【解决方案1】:

    只要信号应该调用槽。这不适用于应该运行插槽或特别是 jobDone 信号的主线程上的 wait()

    确保了解您的应用程序中QThread::exec()QThread::wait() 之间的区别。通常在实际应用程序中,您的线程将循环(运行),而启动它的线程仍在循环。循环在受保护的QThread::exec() 方法中实现。我们通常不需要显式调用exec(),但我们需要允许线程运行。您可以通过使 main 函数使用 QEventLoop 进行循环来做到这一点:

    int main()
    {
    //...
        workerThread.start();
        QEventLoop eventLoop;
    // here you will probably want to hook-up QEventLoop::quit() slot
    // to eventually quit the process
        int returnCode = eventLoop.exec();
    //...
        return returnCode;
    }
    

    这也是错误的:

    connect(&workerThread, SIGNAL(start()), stupidTom, SLOT(doJob()));
    

    相反,您应该创建自己的作业对象并为它执行QObject::moveToThread。这是一个很好的article。所以它应该看起来像:

    connect(&workerThread, SIGNAL(started()), stupidTom, SLOT(doJob()));
    

    我可能理解您为什么尝试以上述方式运行线程。它类似于许多 C++ 示例。您也可以在 Qt 中执行此操作,但您还需要了解您将如何等待 workerThread 完成。 Qt 最流行的线程间交互方式是使用信号和插槽。这就是为什么我们应该在 main 中使用 QEventLoop。但当然还有另一种选择。对于较低级别的普通 C++,您可以使用互斥锁和条件变量来完成相同的操作。或者同样的wait(),但没有涉及任何信号。

    还要观察调试输出,是否所有的连接语句都真正将正确的信号连接到插槽。如果没有连接,它会打印警告。

    【讨论】:

    • 我不明白添加事件循环的原因。首先,很抱歉有一个错字。我正在使用您所指出的 connect(&amp;workerThread, SIGNAL(start()), stupidTom, SLOT(doJob())); 。 worker 对象是我创建的仅用于传递给线程的对象。
    • 参考你提到的那篇文章,我再添加一个连接connect(stupidTom, SIGNAL(jobDone(int)), &amp;workerThread, SLOT(quit()));。当jobDone() 被释放时,它也应该退出线程。没有连接警告。我知道emit jobDone(result); 确实被发出了。但可以肯定的是,main 中的 jobDone() 没有调用,因为未打印调试消息。
    • jobDone() 无法调用,因为跨线程信号需要接收线程中的事件循环才能运行。该事件循环也不能像调用 workerThread.wait() 那样被永久阻塞。
    • @FrankOsterfeld,谢谢。有了你的提醒,我终于明白为什么 Alexander 要求我使用事件循环了。但是我还有一个问题,我将开始一个新帖子。
    • @tom,如果没有事件循环,我们将无法处理插槽中的信号。
    猜你喜欢
    • 2018-04-29
    • 2010-10-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-06
    • 2018-12-14
    • 2019-03-29
    • 2019-01-27
    相关资源
    最近更新 更多