0

I am getting a run-time message

QObject: Cannot create children for a parent that is in a different thread.

when starting a QProcess in a std::thread. The program runs, but I feel that this message will be trouble, eventually.

I saw this one and this other answers (as well as some in python), which I did not see how to apply to my code.

I am using QtCreator, Qt 6.5 LTS and gcc 11.2.0 in Win10. A minimal working example is below. You will have to create an empty GUI window with QPushButton pushButton. The class definition is:

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;
};

And the implementation is:

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");
}

When I click on the button, notepad indeed appears and all looks well. But the Application output console in QtCreator gives me the message "Cannot create children...". For reasons beyond this question, I do want to work with std::thread.

My questions:

  1. What is the trouble that I am sure eventually I'll have because of this message ?

  2. How to get rid of it, while keeping using std::thread ?

11
  • What would be the point of creating a thread that just starts another process, especially since you're trying to detach it? Why can't you just use process.start() from the main thread, or even QProcess::startDetached()? Besides, your QProcess lives in the main thread, and cannot be started from another one (as the error clearly suggests), so you should eventually use moveToThread() for the process object before starting it, but that demands to use an actual QThread. Commented Jul 7 at 23:01
  • @musicamante This is a minimal example to highlight the issue. I'll start a console application from a GUI and monitor its output (stdout and stderr) and update some info in the GUI. All of this while I am doing other stuff. Eventually the console app finishes and tells me so. It will not run forever. Starting from the main thread freezes the UI. Starting detached makes the communication complex.
    – rpsml
    Commented Jul 7 at 23:11
  • @rpsml "Starting from the main thread freezes the UI" That shouldn't happen, unless you're blocking the event loop in some way, possibly in the slots connected to its signals. In any case, you should consider the above (using an actual QThread), but you'd better provide a minimal reproducible example first, demonstrating how the freeze happens when starting the process normally in the main thread. Commented Jul 7 at 23:16
  • @musicamante Thank you for your concern but you are changing my question. I never asked anything about freezing (FYI in my real code there is a waitForFinished() responsible for the freezing). But, again, I never asked about it. My questions are: will this message give me trouble (the code is working despite it)? and How to get rid of it using std::thread? I did provide a minimal reproducible example for the question I asked.
    – rpsml
    Commented Jul 7 at 23:22
  • 1
    @musicamante I don't want to do it this way. I wish I could do it this way. I can't. So I move on.
    – rpsml
    Commented Jul 9 at 7:12

1 Answer 1

2

A QProcess is also a QObject, and each QObject has an associated thread. By default the thread of the QObject is the one that created it. In this case, since it's a member of your own QMainWindow-based class, and will have been created when your main window has been created, it will by default have the same thread as your main window, which is not the thread you are trying to use QProcess from.

Since QProcess internally creates other QObjects, those objects will have been created in the thread that called process.start(). But that is not the thread that QProcess belongs to. And since QObject parent-child relationships can only work for objects in the same thread, you'll get this debug message from Qt, because QProcess can't properly create the desired sub-objects it wants to create.

You have three options here to solve this (in increasing order of simplicity and usefulness):

  • Move the QProcess to the thread that is using it with moveToThread(). Unfortunately, to be 100% conforming to Qt's API, you must call that method from the thread QProcess currently resides in, which would mean a lot of complicated synchronization logic to properly do that, because you'd need to first create the thread, then get the QThread* id of the thusly created thread, then call moveToThread() from the main thread, and then indicate to the thread that it can now use QProcess. Plus you'd need to think about what you want to happen to the process object once the sub-thread exits.

  • Actually create the QProcess in the thread that you want to use the start() method in. In that case you can simply do something like:

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

    Unfortunately that will also cause QProcess object to cease existing at the end of the scope of that function. You could of course return that QProcess to the main thread by allocating it on the heap and with some synchronization logic, but that also gets complicated quite fast.

    Plus, again, you'd need to think about what you'd want to do with the thusly created process when your sub-thread exits.

But this is all way too complicated, because:

  • The easiest solution is to simply use QProcess from the main thread. There is typically no reason to use it from a separate thread, QProcess supports signals and slots, and is therefore easily used asynchronously anyway. There is absolutely no reason to use a separate thread for this.
2
  • I was already expecting this answer. The reason to do this is a comment above but it can be done other ways. I am still intrigued on why the code runs and produces the proper behavior (no UI freezing) and result. I would like to know when will it explode in my face...
    – rpsml
    Commented Jul 8 at 8:01
  • Qt complains here about the process not having the same thread as its sub-objects, but it will still attempt to perform as much as possible. On Windows QProcess creates a QWinEventNotifier as its only child when just using start(), so you'll have: possibly missing exit notifications, memory leak, plus maybe the event notifier using the process handle after it was closed. Plus potential data races due to Qt not expecting you to do things in this manner w.r.t. threading.
    – chris_se
    Commented Jul 8 at 8:15

Not the answer you're looking for? Browse other questions tagged or ask your own question.