【问题标题】:How can someone access a QObject created in the main event loop from a different thread without blocking the main event loop?有人如何在不阻塞主事件循环的情况下从不同的线程访问在主事件循环中创建的 QObject?
【发布时间】:2020-02-17 02:20:58
【问题描述】:

在我的场景中,我有一个复杂的 QObject* 和 QQuickItem* 层次结构,其中包含许多对应用程序都至关重要的子对象。

我希望完成的是:

从特定的 QObject 继承类型执行复杂的 CPU 密集型操作。此操作必须访问层次结构中许多不同 QObject 的属性,并且需要由单独的线程复制和解析大量内存(到了简单地阻塞主事件循环比复制所有内容更有效的地步)结束)。

有没有办法直接访问作为创建新线程的主 QObject 的子级的 QObject?

让我用一些代码澄清一下:

这个sn-p负责在块的网格中找到匹配某些条件的相邻块(这是我的cpu密集型操作,因为它需要在短时间内执行多次)

QList<QQuickItem *> Blockgrid::find_neighbors(QQuickItem *startItem, QList<QQuickItem *> currentNeighbors, QQuickItem *currentItem)
{
  QList<QQuickItem*> neighbors;
  QList<QQuickItem*> new_neighbors;
  Blockrow* startRow = this->get_item_row(startItem);
  int startCell = startRow->get_cell_number(startItem);
  int startColor = startItem->property("blockColor").toInt();
  Blockrow* currentRow = this->get_item_row(currentItem);
  int currentCell = currentRow->get_cell_number(currentItem);
  QList<QQuickItem*> potential_neighbors;
  QQuickItem* topNeighbor = this->find_block(currentRow->rowNumber() - 1, currentCell);
  QQuickItem* bottomNeighbor = this->find_block(currentRow->rowNumber() + 1, currentCell);
  QQuickItem* leftNeighbor = this->find_block(currentRow->rowNumber(), currentCell - 1);
  QQuickItem* rightNeighbor = this->find_block(currentRow->rowNumber(), currentCell + 1);
  if (topNeighbor != nullptr) {
      potential_neighbors << topNeighbor;
    }
  if (bottomNeighbor != nullptr) {
      potential_neighbors << bottomNeighbor;
    }
  if (leftNeighbor != nullptr) {
      potential_neighbors << leftNeighbor;
    }
  if (rightNeighbor != nullptr) {
      potential_neighbors << rightNeighbor;
    }
  foreach (QQuickItem* potentialItem, potential_neighbors) {
      if (potentialItem->property("blockColor") == startColor) {
          if (potentialItem != currentItem) {
              if (!neighbors.contains(potentialItem)) {
                  new_neighbors << potentialItem;


                }
            }
        }
    }

  QList<QQuickItem*> final_neighbors;


  if (new_neighbors.count() > 0) {
      final_neighbors << currentNeighbors;
      foreach (QQuickItem* neighborItem, new_neighbors) {

          if (!final_neighbors.contains(neighborItem)) {
              QList<QQuickItem*> send_neighbors;
              send_neighbors << currentNeighbors << neighborItem << this->find_neighbors(startItem, send_neighbors, neighborItem);
              foreach (QQuickItem* sendItem, send_neighbors) {
                  if (!final_neighbors.contains(sendItem)) {
                     final_neighbors << sendItem;
                    }
                }
            }
        }
    }

  return final_neighbors;
}

重要的不是 CPU 密集型代码,它只是提供一些关于操作复杂性的上下文。

我想要完成的是在单独的线程中运行方法detect_matches


 class Worker : public QObject
  {
      Q_OBJECT

  public slots:
      void detect_matches(Blockgrid* i_grid)
       {
        for (int row=0; row<i_grid->numberOfRows; row++) {
            for (int col=0; col<i_grid->numberOfRows; col++) {
                i_grid->find_block(row, col)->setProperty("opacity", 1.0);
              }
          }
        for (int row=0; row<i_grid->numberOfRows; row++) {
            for (int col=0; col<i_grid->numberOfRows; col++) {
                QList<QQuickItem*> currentNeighbors;

                QList<QQuickItem*> matches;
                matches <<  i_grid->find_neighbors(i_grid->find_block(row,       col), currentNeighbors, i_grid->find_block(row, col));
                if (matches.count() >= 3) {
                    foreach (QQuickItem* matchItem, matches) {
                       matchItem->setProperty("launched", true);
                       i_grid->find_block(row, col)->setProperty("launched",true);


                      }

                  }

              }
          }

      }

   }


};

此线程使用moveTothread 技术启动:


 void Blockgrid::find_matches() {

      Worker *worker = new Worker;
      worker->moveToThread(&workerThread);

      worker->detect_matches(this);

 }

有没有办法让它工作?目前,一旦将 Worker 线程移动到单独的线程中,Worker 线程就无法与 Blockgrid 对象中的对象进行交互。

就像我之前说的,将 Blockgrid 类中的所有数据复制到 Worker 线程中,实际上需要的 CPU 和处理时间比线程完成计算所需的时间要多,这就是我希望找到一种技术的原因这将允许跨线程访问对象。

谢谢。

【问题讨论】:

    标签: qt thread-safety


    【解决方案1】:

    您的 Worker 类的构造函数应该采用指向共享对象的指针;然后 Worker 类应该使用这个注入的共享对象执行活动。

    通过这种方式,您可以控制哪个 Worker 线程将使用哪个共享对象。另外,需要通过互斥锁来处理数据同步。

    【讨论】:

    • 谢谢。这种方法在添加互斥锁时效果很好
    【解决方案2】:

    您可以使用父对象构造您的 QObject,然后访问主 QObject 的子对象。从 QObject 继承的每个类都应该实现一个构造函数,该构造函数接受一个父 QObject*。

    QObject *main = new QObject();
    QObject *child1 = new QObject(main);
    QObject *child2 = new QObject(main);
    

    如果您将 main 保存在您可以访问它的地方,您就可以遍历它的子项。

    https://doc.qt.io/qt-5/qobject.html#children

    foreach (QObject *child : main->children()) {
        // do your stuff with each child
        foreach (QObject *grandChild : main->children()) {
            // do your stuff with each grandchild            
        }
    }
    

    【讨论】:

    • 我试过这个,它似乎可以工作,直到数据不同步(线程运行时删除子对象)不确定原因是线程还是某个异步 JavaScript 代码。谢谢
    • @mike510a 对象不会像那样被删除。如果您不删除它,那么还有其他问题。而且我没有看到您正在使用线程,在这种情况下,请使用 QThread 代替连接的信号/插槽,并且一切都应该像魅力一样工作。 QThread 可以在 Qt 应用程序中进行线程安全通信。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-08
    相关资源
    最近更新 更多