我收到一条运行时消息
QObject:无法为位于不同线程中的父级创建子级。
在
QProcess
中启动 std::thread
时。程序运行了,但我觉得这个消息最终会很麻烦。
我看到了 this one 和 this 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
合作。
我的问题:
我确信最终我会因为这条消息而遇到什么麻烦?
如何摆脱它,同时继续使用
std::thread
?
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
支持信号和槽,因此无论如何都很容易异步使用。绝对没有理由为此使用单独的线程。