QtConcurrent ::运行MainWindow函数,警告消息" QObject :: setParent:无法设置父节点,新父节点位于不同的线程中#34;

时间:2016-07-22 09:57:44

标签: c++ multithreading qt qtconcurrent

我试图使用QtConcurrent :: run在我的MainWindow类中执行一个函数,以便UI在计算过程中保持响应。以下是我实施它的方法:

    void MainWindow::on_calculate_pb_clicked()
    {
        QFuture<void> future = QtConcurrent::run(this,&MainWindow::calculation);
    }

    void MainWindow::calculation()
    {
        progressBar->show();
        loadMap();
        integral.clear();
        positions.clear();
        offset.clear();
        lines = 0;
        for(int i=0;i<paths.size();i++)
        {
            if(i ==0)
            {
                lines = countNumberOfLines(paths.at(i));
            }
            double file = i+1;
            ui->statusBar->showMessage(QString("Processing file %1 of %2").arg(file).arg(paths.size()));

            calculateIntegral(paths.at(i));
            offset.push_back(ui->tableWidget->item(i,1)->text().toDouble());
        }
        makePositionVector();

        plotData(ui->customPlot);
        ui->export_pb->setEnabled(true);
        progressBar->hide();
        ui->statusBar->showMessage("Done",3000);

    }

    void MainWindow::calculateIntegral(QString path)
    {
        QVector<double> mappeddata,tempdata;
        mappeddata.resize(map.size());
        tempdata.resize(numberofdetectors);

        double currentLine = 0;

        QFile File(path);
        if(File.exists()==true){
            File.open(QIODevice::ReadOnly);
            QTextStream in(&File);
            double val;

            while(!in.atEnd())
            {
                for(int j = 0;j<numberofdetectors;j++)
                {
                    in >> val;
                    tempdata[j]+=val;
                    currentLine++;
                    double progress = currentLine/lines*100;
                    progressBar->setValue(progress);
                }

            }

            for(int i =0;i<map.size();i++)
            {
                mappeddata[i] = tempdata.at(map.at(i));
            }
            for(int k = 0;k<numberofdetectors; k++)
            {
                integral.push_back(mappeddata.at(k));
            }

        }
        File.close();
    }

它工作正常,UI响应并且进度条正确更新,但是在输出中我收到错误&#34; QObject :: setParent:无法设置父级,新父级位于不同的线程中#34;很多时候,从循环中执行的东西。

任何想法导致这种情况,或建议更好地实施QtConcurrent :: run?

由于

1 个答案:

答案 0 :(得分:1)

您无法触及工作线程中任何Qt提供的QWidget对象,因为它们的大多数方法都不是线程安全的。

相反,解决这个问题的方法是在worker中进行计算,然后提交将状态更新到主线程的函子。有关详细信息,请参阅this answer

您的代码将成为:

void MainWindow::calculation()
{
    postToThread([this]{ progressBar->show(); });
    loadMap();
    integral.clear();
    positions.clear();
    offset.clear();
    lines = 0;
    for(int i=0;i<paths.size();i++)
    {
        if (i == 0)
            lines = countNumberOfLines(paths.at(i));
        auto file = i+1;
        postToThread([this]{
            ui->statusBar->showMessage(
              tr("Processing file %1 of %2").arg(file).arg(paths.size()));
        });
        calculateIntegral(paths.at(i));
        postToThread([this]{
          offset.push_back(ui->tableWidget->item(i,1)->text().toDouble());
        });
    }
    makePositionVector();

    postToThread([this]{
      plotData(ui->customPlot);
      ui->export_pb->setEnabled(true);
      progressBar->hide();
      ui->statusBar->showMessage("Done",3000);
    });
}

以类似方式修改calculateIntegral,但请确保您不会过于频繁地发布进度更新。

还要确保UI代码不会在其他位置访问您从worker更新的成员。这可能很难,因为您混合了UI和计算。相反,将工作人员抽象为没有UI的QObject,并通过指示进度/状态的信号将其与其他代码连接。您仍将在该对象中使用QtConcurrent::run,但确保没有其他线程访问该对象的私有状态变得更加简单。

要将完成的结果从工人仿函数中推出,您可以发出包含结果的信号。 Data类型应该便宜复制,例如您可以使用QSharedData / QSharedDataPointer来实现它。或者您可以通过QSharedPointer保留它。

class Computation : public QObject {
  Q_OBJECT
  void work() {
    Data data;
    ... // updates data
    emit finished(data);
  }
public:
  Q_SLOT void compute() {
    QtConcurrent::run(&Worker::work, this);
  }
  Q_SIGNAL void finished(Data data);
};

您还可以将结果存储在对象中,并注意在计算处于活动状态时不会访问它们:

class Computation : public QObject {
  Q_OBJECT
  bool m_active { false };
  Data m_data;
  void work() {
    ... // updates m_data
    m_active = false;
  }
public:
  Q_SLOT void compute() {
    m_active = true;
    QtConcurrent::run(&Worker::work, this);
  }
  const Data & data() const {
    Q_ASSERT(! m_active);
    return m_data;
  }
};

当然,如果您将对data()的引用存储在主线程中然后调用compute(),那么您将有未定义的行为,所以不要这样做。

如果任何数据类型是隐式共享容器,如QVectorQString,则应按值返回它们,并且任何访问都是线程安全的:

  QVector<MyData> data() const {
    Q_ASSERT(! m_active);
   return m_data;
  } 

请注意QFile是一个合适的C ++类。它会释放在被破坏时保留的所有资源。手动关闭文件是不必要的:编译器应该在这里帮助你,这是C ++的对象模型的全部要点,例如Java的。