Как кто-то может получить доступ к QObject, созданному в главном цикле событий, из другого потока, не блокируя основной цикл событий? - PullRequest
1 голос
/ 21 октября 2019

В моем сценарии у меня сложная иерархия QObject * и QQuickItem * с множеством дочерних объектов, которые являются критически важными для приложения.

Что я хочу сделать, это:

Выполнить сложную, интенсивную работу с процессором из определенного типа, не зависящего от QObject. Эта операция должна получить доступ к свойствам из множества различных объектов QO в иерархии и потребует копирования и анализа большого объема памяти отдельным потоком (до такой степени, что эффективнее просто блокировать основной цикл событий, чем копировать всеover).

Есть ли способ прямого доступа к объектам QObject, которые являются дочерними по отношению к основному объекту QObject, который создает новый поток?

Позвольте мне пояснить немного с помощью некоторого кода:

Этот фрагмент кода отвечает за поиск соседних блоков в сетке блоков, которые соответствуют определенным условиям (это моя интенсивная работа процессора, потому что она требуетдолжно выполняться много раз за короткий промежуток времени)

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

Важно не код, интенсивно использующий ЦП, который просто служит для обеспечения некоторого контекста сложности операции.

Я пытаюсь выполнить метод 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);

 }

Есть ли способ заставить эту работу? В настоящее время рабочий поток не может взаимодействовать с объектами, которые живут в объекте Blockgrid, после того как он был перемещен в отдельный поток.

Как я уже говорил, копирование всех данных из класса Blockgrid в рабочий потокпо существу потребует больше ресурсов процессора и времени обработки, чем потрачено потоком для завершения вычислений, поэтому я хочу найти метод, который позволит обращаться к объектам через потоки.

Спасибо.

Ответы [ 2 ]

1 голос
/ 21 октября 2019

Вы можете создать свой 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            
    }
}
0 голосов
/ 21 октября 2019

Конструктор вашего класса Worker должен взять указатель на общий объект;и затем класс Worker должен выполнить действия с этим внедренным общим объектом.

Таким образом, вы можете контролировать, какой рабочий поток будет использовать какой общий объект. Также необходимо позаботиться о синхронизации данных через мьютекс.

...