【问题标题】:How to do proper threading in QT GUI application?如何在 QT GUI 应用程序中进行正确的线程化?
【发布时间】:2013-12-31 10:05:09
【问题描述】:

我想同时运行两个线程。当我将程序作为 QT 控制台应用程序运行时,我成功地做了同样的事情。 这是用于多线程的 QT 控制台应用程序的工作代码:-

我的对象.h

#ifndef MYOBJECT_H
#define MYOBJECT_H

#include <QObject>
#include <QDebug>
#include <QThread>

class MyObject : public QObject
{
    Q_OBJECT
public:
    explicit MyObject(QObject *parent = 0);
    void doSetup(QThread &cThread);
    void doSetup2(QThread &cThread2);
signals:

public slots:
    void doWork();
    void doWork2();
};

#endif // MYOBJECT_H

main.cpp:

#include <QtCore/QCoreApplication>
#include <QThread>
#include <myobject.h>
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QThread cThread, cThread2;
    MyObject cObject, cObject2;

    cObject.doSetup(cThread);
    cObject.moveToThread(&cThread);
    cObject2.doSetup2(cThread2);
    cObject2.moveToThread(&cThread2);
    cThread.start();
    cThread2.start();
    qDebug()<<"hello there ";
    return a.exec();
}

我的对象.cpp

#include "myobject.h"
#include <QThread>
#include "tftpServer.h"

MyObject::MyObject(QObject *parent) :
    QObject(parent)
{
}

void MyObject::doSetup(QThread &cThread)
{
    connect(&cThread, SIGNAL(started()), this, SLOT(doWork()));
}

void MyObject::doSetup2(QThread &cThread2)
{
    connect(&cThread2, SIGNAL(started()), this, SLOT(doWork2()));
}
void MyObject::doWork()
{
    for(int i=0; i<1000; i++)
    {
        qDebug()<<"******************Thread 1";


    }

}
void MyObject::doWork2()
{
    for(int i=1000; i<2000; i++)
    {
        qDebug()<<"Thread 2************************";


    }

}

这是输出:

******************Thread 1
Thread 2************************
******************Thread 1
Thread 2************************
******************Thread 1
Thread 2************************
******************Thread 1
Thread 2************************

..等等

现在,当我使用几乎相同的代码并作为 QT GUI 应用程序运行时,线程不会同时运行,而是一个接一个地运行。代码如下:

主窗口.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QDebug>
#include <QThread>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();
    void doSetup(QThread &cThread);
    void doSetup2(QThread &cThread2);
private:
    Ui::MainWindow *ui;

public slots:
    void doWork();
    void doWork2();
};

#endif // MAINWINDOW_H

main.cpp

#include <QtGui/QApplication>
#include "mainwindow.h"
#include <QThread>
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    QThread cThread, cThread2;
    MainWindow cObject, cObject2;

    cObject.doSetup(cThread);
    cObject.moveToThread(&cThread);
    cObject2.doSetup2(cThread2);
    cObject2.moveToThread(&cThread2);
    cThread.start();
    cThread2.start();

    MainWindow w;
    w.show();

    return a.exec();
}

主窗口.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QThread>
#include <qthread.h>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    qDebug()<<"gui running";
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::doSetup(QThread &cThread)
{
    connect(&cThread, SIGNAL(started()), this, SLOT(doWork()));
}

void MainWindow::doSetup2(QThread &cThread2)
{
    connect(&cThread2, SIGNAL(started()), this, SLOT(doWork2()));
}
void MainWindow::doWork()
{
    //QThread::sleep(100);
    for(int i=0; i<1000; i++)
    {
      qDebug()<<"******************Thread 1";
//      qDebug()<<i;

    }

}
void MainWindow::doWork2()
{
    for(int i=1000; i<2000; i++)
    {
      qDebug()<<"Thread 2************************";
//        qDebug()<<i;

    }

}

这是输出:

gui running 
gui running 
QObject::moveToThread: Widgets cannot be moved to a new thread
QObject::moveToThread: Widgets cannot be moved to a new thread
gui running 
******************Thread 1 
******************Thread 1 
******************Thread 1 
******************Thread 1

(等等……)

******************Thread 1
Thread 2************************ 
Thread 2************************ 
Thread 2************************ 
Thread 2************************ 
Thread 2************************ 
Thread 2************************ 

(等等...) 请注意,我所做的黑白控制台和 gui 应用程序的唯一区别是将基类 QObject 更改为 QMainWindow,因此创建的线程对象分别是 QObject 和 QMainWindow 的对象,用于控制台和 GUI 应用程序。

希望我足够清楚,能否请您告诉我我犯了什么错误,即使线程一个接一个地运行,而不是像控制台应用程序那样并发运行?

谢谢。

【问题讨论】:

  • 另外,在输出中:QObject::moveToThread: Widgets cannot be move to a new thread,我猜这行是在解释问题。
  • 你想达到什么目的,doWork 只能运行一次?它是否访问任何私有成员变量?

标签: c++ multithreading qt qobject qmainwindow


【解决方案1】:

从 QWidget 继承的任何类都不能驻留在主 GUI 线程以外的线程中。实际上这就是输出所说的。您的解决方案是一个糟糕的设计。将工作对象(继承自QObject,驻留在另一个线程中)和可视对象(继承自QWidget,驻留在GUI线程中)分开,遵循“一类一责”的原则。

Qt 中多线程的另一个问题是,任何 QObject 都不能驻留在与其父级不同的线程中。这是很自然的,因为父对象是子对象的所有者,最重要的是它负责销毁子对象。要启用此功能,孩子不能在其他线程中。否则,例如,父母可能会破坏正在执行的孩子。

还有更多的捕获,例如 QPixmap 不能在主 GUI 以外的其他线程中。

【讨论】:

  • @Vladmir,如果我的理解正确,请告诉我:1. QObject 的对象,即 QWidget(在 GUI 应用程序中)的 cThread(在控制台应用程序中)对象应该是分开的。但是在 GUI 应用程序中,我根本没有使用 QObject,ryt? 2. QObject 类的对象必须与父对象驻留在同一线程中。这里的父类是哪个?
  • Sai:您可以像在控制台中一样在 GUI 应用程序中使用 QObjects。 QWidgets 主要用于视觉表示,它们绝不是 QObjects 的替代品。您示例中的 QObject 应该在非 GUI 线程中做一些工作(例如打印出 qDebug...)。所以基本上保持你的结构类似于你对控制台应用程序的结构。然后你只需要考虑你想在 QWidget 中显示什么(这就是你有 QWidget 的原因,对吧?)。
  • Sai: ...假设您希望例如在 QLabel 中显示一个字符串(例如),而不是使用 qDebug 打印它...在这种情况下,您的 QObject 的 doWork 创建了该字符串,然后您应该使用信号槽将其传递给您的 QWidget 或 QLabel。在某些情况下,即使没有信号槽机制,您也可以达到相同的效果,但它通常有线程安全漏洞。
  • Sai: ...子父不是指继承,所以谈论哪个类是父类是没有意义的。 Qt 中的每个 QObject 可以(但不需要)有一个父对象。 Parent 是您在调用任何 QObject 的构造函数时通常传递的对象。有时有一个父母很方便,因为父母可以处理破坏,所以你确实会遇到内存泄漏。但是你需要记住,孩子必须与父母在同一个线程中。如果 QObject 没有父对象,那么您可以将它移动到您喜欢的任何线程。
  • 当然,我在上一条评论中的意思是“...不要发生内存泄漏...”。 :)
猜你喜欢
  • 2011-08-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多