Qt Невозможно переместить цель в поток

Я сталкиваюсь со странной ошибкой в ​​моем приложении Qt 5.7 (в Windows 10), и обычных виновников такого поведения нигде не найти:

  • У объекта, который перемещается, есть родитель - скорее всего, не тот случай
  • Попытка вытянуть объект в поток вместо его нажатия - это причина ошибки, однако я понятия не имею, откуда она исходит

Полное сообщение об ошибке

QObject::moveToThread: текущий поток (0x2afcca68) не является потоком объекта (0x34f4acc8). Невозможно перейти к целевому потоку (0x34f4adc8)

QObject:: setParent: невозможно установить родителя, новый родитель находится в другом потоке

и вот еще мой код:

main.cpp

#include <QApplication>
#include <QQuickItem>
#include "CustomQuickWidget.h"

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    QApplication app(argc, argv);

    const QUrl source = QUrl(QLatin1String("qrc:/main"));
    CustomQuickWidget widget(source);

    return app.exec();
}

main (псевдоним для main.qml):

// You can put any random QML content in this case really as long as it doesn't create a window since the CustomQuickWidget does that.
Rectangle {
    id: window
    visible: true
    width: 600
    height: 480
}

CustomQuickWidget.cpp

#include "CustomQuickWidget.h"
#include <QQuickItem>

CustomQuickWidget::CustomQuickWidget(const QUrl &source, QWidget *parent) : QQuickWidget(source, parent) {
    // Setup the recognizer
    this->airWheelRecognizer = new QAirWheelGestureRecognizer();
    this->airWheelType = QGestureRecognizer::registerRecognizer(airWheelRecognizer);
    // and turn on grabbing for all the supported gestures
    grabGesture(airWheelType);
    grabGesture(Qt::SwipeGesture);
    grabGesture(Qt::TapGesture);

    // Create thread and device worker
    this->deviceThread = new QThread(this);
    this->deviceWorker = new DeviceMapper(this, Q_NULLPTR); // NOTE: this here is NOT for parent. The constructor's signature for this class is: DeviceMapper(QObject* receiver, QList<Qt::GestureType>* gestureIDs, QObject* parent = Q_NULLPTR)
    this->deviceWorker->init();

    // Create timer that will trigger the data retrieval slot upon timeout
    this->timer = new QTimer();
    this->timer->setTimerType(Qt::PreciseTimer);
    this->timer->setInterval(5);

    // Move timer and device mapper to other thread
    this->timer->moveToThread(this->deviceThread);
    this->deviceWorker->moveToThread(this->deviceThread); // FIXME For unknown reason: QObject::moveToThread: Current thread (...) is not the object's thread. Cannot move to target thread

    // Connect widget, timer and device mapper
    createConnections();

    // Run thread
    this->deviceThread->start();

    // Connect device and start data retrieval
    QTimer::singleShot(0, this->deviceWorker, &(this->deviceWorker->slotToggleConnection));
    QTimer::singleShot(0, this->deviceWorker, &(this->deviceWorker->slotToggleRun));

    this->show();
}

CustomQuickWidget::~CustomQuickWidget()
{
    if (this->deviceThread) {
        this->deviceThread->quit();
        this->deviceThread->wait();
    }
}

void CustomQuickWidget::createConnections()
{
    connect(this->timer, SIGNAL(timeout()),
            this->deviceWorker, SLOT(slotRetrieveData()));

    connect(this->deviceThread, SIGNAL(started()),
            this->timer, SLOT(start()));
    connect(this->deviceThread, SIGNAL(finished()),
            this->deviceWorker, SLOT(deleteLater()));
    connect(this->deviceThread, SIGNAL(finished()),
            this->deviceThread, SLOT(deleteLater()));
}

bool CustomQuickWidget::event(QEvent* event) {
    if (event->type() == QEvent::Gesture) { 
        bool res = gestureEvent(static_cast<QGestureEvent*>(event)); // Not important so not included as code here
        return res;
    }

    return QWidget::event(event);
}

Как вы можете видеть, у меня здесь происходит типичная рабочая нить. Я убедился, что мой работник (здесь DeviceMapper) не имеет родителя. Он также создается внутри моего виджета (где QThread также создан), но перенесен в поток вместе с таймером.

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

  • Там нет такой ошибки, когда this->timer->moveToThread(this->deviceThread); называется
  • Этот же код работает без каких-либо проблем в другом проекте, который является проектом subdirs - один подпроект создает общую библиотеку (которую я использую и в этом проекте), а другой - приложение, которое использует библиотеку.

Единственная разница между моим другим приложением и этим - использование QQuickWidget (вместо QWidget) а также QML, Я совсем новичок в QML и это тоже мой первый QQuickWidget так что я мог бы упустить некоторые очевидные настройки, которые нужно "активировать".

Я также добавил

cout << this->deviceWorker->thread()->currentThreadId() << endl;
cout << this->thread()->currentThreadId() << endl;

прямо перед this->deviceWorker->moveToThread(this->deviceThread); и я получил

0x18b0
0x18b0

что означает, что до moveToThread(...) мой объект принадлежит той же нити, где QThread создается экземпляр. Печать идентификатора нити после moveToThread(...) возвращает тот же результат, но это ожидается из-за неудачной попытки правильно переместить объект в другой поток.


ОБНОВИТЬ:

Сообщение об ошибке появляется ТОЛЬКО при сборке в режиме выпуска, однако, независимо от типа сборки у меня ошибка все еще присутствует.

1 ответ

Решение

Мне удалось решить мою проблему, точно определив КОГДА это происходит.

В конце прошлой недели приложение, которое я писал, неожиданно заработало, поэтому меня беспокоило, почему все, что произошло до того, я позволил этому случиться. Я не изменил ни кода библиотеки (за исключением пары комментариев в моем коде, которые, очевидно, не могут повлиять на сам код), ни C++ код моего QML приложение. Все, что я изменил, было моим QML но таким образом, что на самом деле не относится к C++ Код внизу. Единственное, что я изменил, был тип сборки. Однако я не заметил этого на прошлой неделе.

Вчера я начал работать над новым проектом. И сразу после первого запуска у меня возникла та же проблема. Это сводило меня с ума. Поэтому я начал анализировать свой код (@Kuba Ober, извините, приятель, но опубликовать полный код или даже небольшой фрагмент библиотеки невозможно, иначе я бы сделал это (даже если это пара сотен строк реального кода (исключая такие вещи, как комментарии и пустые строки)). Я проверил и перепроверил отношения родитель-потомок, но не смог найти ничего, что могло бы дать мне хотя бы небольшую подсказку, когда и почему это происходит. Я также проанализировал стек, чтобы лучшие из моих способностей, но все напрасно.

Затем меня поразило... Я уже упоминал выше, что мой предыдущий проект начал работать внезапно после изменения типа сборки. И действительно, это было источником всего зла в моей ситуации. То, как я добавляю свою библиотеку к своим проектам (исключая исходную, которая вместе с библиотекой является частью той же subdir проект), создав папку в корневом каталоге моего нового проекта под названием libs и копирование связанных вещей к нему. Теперь, когда я закончил работу над своей библиотекой и провел некоторое тестирование, я, очевидно, решил перейти на выпуск сборки. Однако я скопировал сборку библиотеки в release режим для построения проекта в debug режим Итак, после нескольких перестроений и копирования библиотеки тут и там я обнаружил, что смешивание типов сборки приложения, которое использует библиотеку и саму библиотеку, приводит к этой проблеме.

Я знаю, что смешивать типы сборок - это плохая идея, и обычно я этого не делаю, но на этот раз это просто ускользнуло от меня и стало полной катастрофой. Я не знаю, что происходит внутри, когда и приложение с типом сборки X, и библиотека с типом сборки Y смешаны, но результатом в моем случае стала ошибка, которую я разместил в этой теме.

Спасибо за помощь. Я многому научился благодаря вашим комментариям! Несмотря на то, что в моем случае отладка не была необходима, вы мне благодарны.:)

Другие вопросы по тегам