QProcess 和 std::thread - 无法为位于不同线程中的父级创建子级

问题描述 投票:0回答:1

我收到一条运行时消息

QObject:无法为位于不同线程中的父级创建子级。

QProcess
中启动
std::thread
时。程序运行了,但我觉得这个消息最终会很麻烦。

我看到了 this onethis other 答案(以及 python 中的一些答案),我没有看到如何应用于我的代码。

我在Win10中使用QtCreator、Qt 6.5 LTS和gcc 11.2.0。下面是一个最小的工作示例。您必须使用

QPushButton pushButton
创建一个空的 GUI 窗口。类定义为:

class MainWindow : public QMainWindow
{
  Q_OBJECT
    
public:
  MainWindow(QWidget *parent = nullptr);
  ~MainWindow();
    
private slots:
  void clicked(bool);
    
private:
  Ui::MainWindow *ui;
    
  void launchNotepad();
   
  std::thread th;
  QProcess process;
};

实现是:

MainWindow::MainWindow(QWidget *parent)
  : QMainWindow(parent)
  , ui(new Ui::MainWindow)
{
  ui->setupUi(this);
  connect(ui->pushButton, SIGNAL(clicked(bool)), this, SLOT(clicked(bool)));
}

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

void MainWindow::clicked(bool)
{
  th = std::thread{&MainWindow::launchNotepad,this};
  th.detach();
}

void MainWindow::launchNotepad()
{
  process.start("notepad");
}

当我点击按钮时,记事本确实出现了,一切看起来都很好。但是 QtCreator 中的应用程序输出控制台给我消息“无法创建子项...”。出于这个问题之外的原因,我确实想与

std::thread
合作。

我的问题:

  1. 我确信最终我会因为这条消息而遇到什么麻烦?

  2. 如何摆脱它,同时继续使用

    std::thread

c++ multithreading qt qprocess stdthread
1个回答
0
投票

A

QProcess
也是一个
QObject
,每个
QObject
都有一个关联的线程。默认情况下,
QObject
的线程是创建它的线程。在这种情况下,由于它是您自己的基于
QMainWindow
的类的成员,并且将在创建主窗口时创建,因此默认情况下它将与您的主窗口具有相同的线程,而不是您的线程正在尝试使用
QProcess
from.

由于

QProcess
在内部创建了其他
QObject
,这些对象将在调用
process.start()
的线程中创建。但这不是
QProcess
所属的线程。由于
QObject
父子关系只能适用于同一线程中的对象,因此您将从 Qt 中收到此调试消息,因为
QProcess
无法正确创建它想要创建的所需子对象。

您可以使用三个选项来解决此问题(按照简单性和实用性的递增顺序):

  • QProcess
    移动到使用
    moveToThread()
    使用它的线程。不幸的是,要 100% 符合 Qt 的 API,您必须从当前所在的线程
    QProcess
    调用该方法,这意味着要正确执行此操作需要大量复杂的同步逻辑,因为您需要首先创建线程,然后获取由此创建的线程的
    QThread*
    id,然后从主线程调用
    moveToThread()
    ,然后向线程指示它现在可以使用
    QProcess
    。另外,您需要考虑一旦子线程退出,您希望对
    process
    对象发生什么。

  • 实际上在您想要使用

    QProcess
    方法的线程中创建
    start()
    。在这种情况下,您可以简单地执行以下操作:

    void MainWindow::launchNotepad()
    {
       QProcess process;
       process.start("notepad");
    }
    

    不幸的是,这也会导致

    QProcess
    对象在该函数作用域结束时不再存在。当然,您可以通过在堆上分配 QProcess 并使用一些同步逻辑将其返回到主线程,但这也会很快变得复杂。

    另外,您还需要考虑当子线程退出时您想要如何处理由此创建的进程。

但这太复杂了,因为:

  • 最简单的解决方案是在主线程中简单地使用
    QProcess
    。通常没有理由从单独的线程使用它,
    QProcess
    支持信号和槽,因此无论如何都很容易异步使用。绝对没有理由为此使用单独的线程。
© www.soinside.com 2019 - 2024. All rights reserved.