У меня проблема с многопоточностью Qt. У меня есть класс, который я хочу в качестве темы
//protdata.cpp
class ProtData : public QObject
{
Q_OBJECT
private:
QList<ProtDataInputHandler *> _inputs;
public:
ProtData();
void addInput();
....
};
void ProtData::addInput(QIODevice *input, bool network_order)
{
_inputs.append(new ProtDataInputHandler());
}
У меня есть другой класс display.cpp, где я создаю экземпляр объекта protdata как поток, используя moveToThread ();
//display.cpp
...
QThread* newThread = new QThread();
_protdata->moveToThread(newThread);
newThread->start();
...
В какой-то момент в display.cpp:
//display.cpp
....
_protdata->addInput();
Когда я выполняю метод addInput, я получаю следующую ошибку:
QObject: Невозможно создать дочерние элементы для родителя, который находится в другом потоке.
(Родитель — ProtData (0x19bba50), родительский поток — QThread (0x19b3c18), текущий поток — QThread (0x1f08930).
Что не так? Я должен также переместить класс ProtDataInputHandler в newThread? Как?
Спасибо
AddInput должен вызываться только в procdata.cpp.
Вы можете использовать сигнал для вызова вашей функции, если вы определите ее как слот:
// display.h
signals :
void addInputSignal();
// display.cpp
QObject::connect(this, SIGNAL(addInputSignal()), newThread, SLOT(addInput()));
// ...
emit addInputSignal();
// protdata.h
public slots:
void addInput();
Попробуйте пометить свою функцию как Q_INVOKABLE
:
Q_INVOKABLE void addInput();
Теперь вы можете использовать QMetaObject::invokeMethod
для того, чтобы сделать перекрестный вызов метода. Переход к другому потоку не влияет на вызовы, которые вы делаете напрямую, но влияет на события и сигнальные вызовы, такие как этот:
QMetaObject::invokeMethod(_procdata, "addInput", Qt::AutoConnection);
Чтобы передать аргументы, вам нужно добавить дополнительные аргументы QMetaObject::invokeMethod
, как правило, используя Q_ARG
макрос.
Ваша проблема — эта строка в коде:
_inputs.append(new ProtDataInputHandler());
_inputs
были созданы в главном потоке, когда ваши ProtData были созданы в основном потоке.
Однако вы вызываете метод addInput (), работающий с этим, после того, как ваш подкласс ProtData QObject был перемещен в другой поток.
Лучшее решение будет использовать механизм сигнального слота в Qt.
По сути, вы должны определить слот в вашем классе ProtData, а также сигнал. Вы отправите сигнал из addInput, и ваш соответствующий слот будет помещен в очередь цикла событий Qt для выполнения.
Затем вы должны установить соединение между сигналом и слотом в конструкторе ProtData.
Применяя всю эту теорию на практике, вы изменили бы свой код следующим образом:
//protdata.cpp
class ProtData : public QObject
{
Q_OBJECT
private:
QList<ProtDataInputHandler *> _inputs;
public:
explicit ProtData(QObject *parent = 0);
void addInput();
public signals:
void appendInput();
public slots:
void handleAppendInput();
....
};
ProtData::ProtData(QObject *parent)
: QObject(parent)
{
...
connect(this, SIGNAL(appendInput()), SLOT(handleAppendInput()));
...
}
void ProtData::handleAppendInput()
{
_inputs.append(new ProtDataInputHandler());
}
void ProtData::addInput(QIODevice *input, bool network_order)
{
emit appendInput();
}
Обратите внимание, что я также исправил другие проблемы в вашем коде:
Я сделал ваш конструктор явным, как и должно быть из-за равномерной инициализации с C ++ 11 и далее.
Вы забыли иметь родительский аргумент QObject, который назначен базовому классу, поэтому я также изменил его, имея значение по умолчанию «no parent», если оно не указано явно.