【问题标题】:QThread blocks the event loopQThread 阻塞事件循环
【发布时间】:2013-11-07 16:40:36
【问题描述】:

我最近刚开始使用 QThreads API,遇到了一个奇怪的问题。

我用重新实现的 run() 方法创建了 QThread 的子类 这里是:

void ThreadChecker::run()
{
    emit TotalDbSize(1000);

    for (int i = 0; i < 1000; i++)
    {
        QString number;
        number.setNum(i);
        number.append("\n");
        emit SimpleMessage(number);
        //pausing is necessary, since in the real program the thread will perform quite lenghty tasks
        usleep(10000);
    }
}

下面是调用这个线程的代码:

ThreadChecker thread;


connect(&thread, SIGNAL(TotalDbSize(int)), this, SLOT(SetMaximumProgress(int)));
//This slot writes the message into the QTextEdit
connect(&thread, SIGNAL(SimpleMessage(QString)), this, SLOT(ProcessSimpleMessage(QString)));

thread.start();

我打算让 QTextEdit 每 10 毫秒更新一次。但相反,该程序仅滞后 10 秒,然后所有信号立即涌现。此外,当程序滞后时,它的行为就像事件循环被阻塞(按钮不会[按下,调整大小不起作用等)

我在这里错过了什么?

【问题讨论】:

  • 尝试将以下内容添加到您的ThreadChecker ctor:moveToThread(this)。但是请注意,这不是解决方案。这只是为了检查它是否有帮助,以便更好地了解您的问题。
  • 如果 ctor 指的是构造函数,那么它并没有解决问题
  • 你为什么不简单地使用QTimer?
  • 正如cmets中所说,我使用暂停,因为在原始程序中是为了计算大块数据,通过函数有效地冻结进度
  • 我只看到这种行为的一种解释。我怀疑最后一段代码在不包含事件循环执行的代码分支中,因此线程块的析构函数(调用 waitFor)你的主线程,直到你的工作完成。当您使用来自 ixSci 的代码时,您可能已经更改了该代码,并且在调用线程的析构函数之前运行事件外观。无论如何 ixScis 代码是正确的,您可能会导致一些错误。

标签: qt qthread


【解决方案1】:

试试下面的代码:

class Updater: public QObject
{
    Q_OBJECT
public slots:
    void updateLoop()
    {
        emit TotalDbSize(1000);

        for (int i = 0; i < 1000; i++)
        {
            QString number;
            number.setNum(i);
            number.append("\n");
            emit SimpleMessage(number);
            //pausing is necessary, since in the real program the thread will perform quite lenghty tasks
            usleep(10000);
        }
    }
signals:
    void TotalDbSize(...);
    void SimpleMessage(...);

};
...
QThread updaterThread;
Updater updater;
updater.moveToThread(&updaterThread);
connect(&updater, SIGNAL(TotalDbSize(int)), this, SLOT(SetMaximumProgress(int)));
//This slot writes the message into the QTextEdit
connect(&updater, SIGNAL(SimpleMessage(QString)), this, SLOT(ProcessSimpleMessage(QString)));
connect(&updaterThread, SIGNAL(started()), &updater, SLOT(updateLoop()));
updaterThread.start();

不过,我没有检查它。请注意,您应该保证updaterThreadupdater 不会超出范围。

======

为什么问题中的代码不起作用?我只能猜测:您将信号附加到 QThread 对象,当您执行connect 时,您有直接连接,因为threadthis 在同一个线程中。因此,当您发出信号时,直接连接有效并且您从 GUI 线程外部更新您的 TextBox,这是错误的,可能会导致任何结果。但是请注意,我的猜测可能是错误的,并且可以通过调试器找到确切的原因。

另请阅读Threads, Events and QObjects 文章。这是一篇了解如何正确使用 Qt 线程的好文章。

【讨论】:

  • 我试过了,效果很好。谢谢。现在我只是好奇为什么我的原始代码没有按预期工作?从我收集到的信息来看,子类化 QThread 已经过时并且违反 OOP 原则,但没有错
  • @WereWind,除非你想改变线程的控制方式,否则子类化 QThread 是错误的; blog.qt.digia.com/blog/2010/06/17/youre-doing-it-wrong
  • 您也会发现这很有用:mayaposch.wordpress.com/2011/11/01/…
  • @WereWind unless you want to change how threads are controlled, subclassing QThread is wrong,是吗?见thisthis
  • @thuga,我相信 Woboq 的帖子是错误的。线程不是 Qt 的发明,在其他地方(至少我知道的那些地方)没有人继承线程。您应该将工作转移到线程而不是子类化它。线程是 OS 原始的,为什么任何人都想继承它?此外,当您看到这样的内容时:connect(workerThread, SIGNAL(finished()), workerThread, SLOT(deleteLater())); 您应该发出警报。它是 C++,你最好有类似 C++ 的对象生命周期机制。我认为deleteLater() 应该在极少数情况下使用。
猜你喜欢
  • 1970-01-01
  • 2012-02-20
  • 1970-01-01
  • 2013-08-16
  • 1970-01-01
  • 1970-01-01
  • 2016-04-21
相关资源
最近更新 更多