由于另一个QThread,QApplication线程冻结

Ant*_*dev 7 qt multithreading qthread qtcore

在我的Qt应用程序中,我创建了一个QThread应该定期执行一些繁重的计算任务.主QApplication线程应该维护GUI(不包括在示例中)并执行一些定期更新.两个线程都有自己的定时器来启用常规update()调用.

问题:当工作线程的计算工作负载超过某个临界值时,我的主线程停止接收计时器事件.

示例代码如下.当主线程调用update()时,它输出"Main",工作线程调用"Worker".如果你运行它,你会看到"工人"定期打印,"主"正好出现两次(一个在开头,一个在约5秒).在全功能GUI应用程序的情况下,这实际上意味着完全GUI冻结.

一些观察.

  1. 通过对内循环(而不是1000)设置100限制来减少工作量将解决问题(两个update()方法都将定期调用).
  2. 将工作线程定时器信号的连接类型设置为Qt :: DirectConnection将解决问题.

所以,你可以看到我有几个解决方法,但我很感激任何人解释我的原始代码有什么问题.我希望线程能够独立地执行它们的事件循环.我知道我通过一个很长的update()操作来阻止工作线程事件循环,但为什么它在世界上会影响主线程呢?

PS是的,我知道QConcurrent替代方案.但我只是想明白.

TEST.CPP

#include <windows.h>
#include <QApplication>

#include "test.h"

HANDLE mainThread_ = INVALID_HANDLE_VALUE;
QApplication *app_ = 0;
MyObj *obj_ = 0;
MyThread *thread_ = 0;

MyObj::MyObj()
    : timer_(0)
{
    timer_ = new QTimer(0);
    connect(timer_, SIGNAL(timeout()), this, SLOT(update()));
    timer_->start(10);
}

void MyObj::update()
{
    printf("Main\n");
}

void MyThread::run()
{
    timer_ = new QTimer(0);
    connect(timer_, SIGNAL(timeout()), this, SLOT(update()));
    timer_->start(10);

    exec();
}

void MyThread::update()
{
    printf("Worker\n");

    // do some hard work
    float f = 0.f;
    for (int i=0; i < 100000; ++i)
    {
        for (int j=0; j < 1000; ++j)
        {
            f += i * j;
        }
    }
}

int main()
{
    int argc = 0;
    app_ = new QApplication(argc, 0);

    obj_ = new MyObj();
    thread_ = new MyThread();
    thread_->start();

    QApplication::exec();

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

test.h

#include <QTimer>
#include <QThread>

class MyObj : public QObject
{
    Q_OBJECT

public:
    MyObj();

public slots:
    void update();

private:
    QTimer *timer_;
};

class MyThread : public QThread
{
    Q_OBJECT

public:
    void run();

public slots:
    void update();

private:
    QTimer *timer_;
};
Run Code Online (Sandbox Code Playgroud)

UPD:我从尊敬的成员那里得到了一些答案(请在下面阅读).现在我想澄清一下错误的想法特别破坏了我的代码.

正如您所看到的,该计划有两个线程,每个线程定期运行一些update()过程.我的错误是将update()视为一些过程,而且它是一个插槽.特定对象的一个​​槽,它有自己的线程关联,这意味着它的主体将在该线程中执行(除非用Qt :: DirectConnection调度信号).现在,似乎我已经用计时器完成了它们 - 每个都属于不同的线程 - 但是用update()搞砸了.所以我最终在主线程中执行了两个update()程序.显然,在某些时候,事件循环会被定时器事件淹没,并且永远不会完成迭代.

至于解决方案.如果你已经读过"你做错了"(你确实应该),你知道在一个没有从QThread子类化但是单独创建并通过moveToThread连接到QThread的对象中实现所有逻辑是相当方便的( ).我个人认为,如果你记住你的对象只控制线程但不属于它,那么从QThread继承子类没什么问题.因此,它不是您希望在该线程中执行的代码的位置.

The*_*ght 3

这里的第一个问题是你是从 QThread 继承的,所以正如它在这里所说的,“你做错了”。

您遇到的问题源于线程关联性(对象在哪个线程上运行)。例如,如果您要从 QThread 继承并在构造函数中创建对象,而不是该对象的父级,则该对象将在主线程中运行,而不是在新线程中运行。所以在 MyThread 构造函数中你会有:-

MyThread::MyThread()
    : timer_(0)
{
    timer_ = new QTimer(0);
    connect(timer_, SIGNAL(timeout()), this, SLOT(update()));
    timer_->start(10);
}
Run Code Online (Sandbox Code Playgroud)

这里的定时器(timer_)将运行在主线程上,而不是新线程上。为了避免重复,我之前的答案之一在这里解释了线程亲和力。

解决该问题的最佳方法是将您的类更改为从 QObject 继承,然后将该对象移动到新线程。

  • @AntonZherzdev 如果您计划使用插槽,则永远不应该子类化 QThread。正如您所注意到的,槽将在“MyThread”对象所在的线程中执行,而不是在它管理的线程中执行。[文档中也有说明](http://qt-project.org/doc/qt-5.0/qtcore/qthread.html#details)。`在 QThread 子类中实现新槽很容易出错并且不鼓励` (2认同)