Интерфейс Qt зависает при фоновой задаче

Мне нужно решить интересную задачу. Я использую Qt 5 для одного из своих проектов для чтения информации в сети. Я читал устройства Modbus и прочее, но настоящая проблема возникает, когда сеть недоступна.

Интерфейс зависает, и я не могу с ним взаимодействовать. Сетевые вещи выполняются в отдельном потоке, или я так думаю. Вот пример кода:

class TwidoDevice : public QObject
{
    Q_OBJECT
public:
    explicit TwidoDevice
........ And some useful code

Использование класса (основного интерфейса) в Window.cpp:

L1Thread = new QThread();
L1Thread->start();
L1TWD = new TwidoDevice(L1TWD_settings,
                        L1TWD_Name,
                        PercentRegisters,
                        TotalsRegisters,
                        db, 1);
L1TWD->moveToThread(L1Thread);
connect(this, SIGNAL(startReading()), L1TWD, SLOT(startFired()), Qt::DirectConnection);

В этом коде startFired() начните чтение устройств в сети.

В какой-то другой функции в Window.cpp:

emit startReading()

Когда этот код выполняется, интерфейс зависает, хотя я переместил объект L1TWD в QThread.

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

Кто-нибудь сталкивался с такой же проблемой и как это решить?

Спасибо, что потратили время на чтение моего вопроса!


person melanholly    schedule 04.12.2014    source источник
comment
Вы пробовали connect(this, SIGNAL(startReading()), L1TWD, SLOT(startFired()));? Пожалуйста, используйте currentThread, чтобы распечатать это, чтобы убедиться, что вы переместились правильно.   -  person lpapp    schedule 04.12.2014


Ответы (2)


Это основная проблема:

connect(this, SIGNAL(startReading()), L1TWD, SLOT(startFired()), Qt::DirectConnection);

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

  • Используйте соединение по умолчанию, которое не будет блокироваться между потоками, только внутри одного и того же потока. Итак, вы бы написали что-то вроде этого:

connect(this, SIGNAL(startReading()), L1TWD, SLOT(startFired()));

  • Во-вторых, вы могли бы убедиться, что ваш поток не зависает, когда возникает какая-то проблема с сетью.

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

[статические] QThread * QThread::​currentThread()

Возвращает указатель на QThread, который управляет текущим исполняемым потоком.

и это:

[статические] Qt::HANDLE QThread::​currentThreadId()

Возвращает дескриптор потока, выполняющегося в данный момент.

Предупреждение: дескриптор, возвращаемый этой функцией, используется для внутренних целей и не должен использоваться ни в каком коде приложения.

Предупреждение. В Windows возвращаемое значение является псевдодескриптором текущего потока. Его нельзя использовать для числового сравнения. т. е. эта функция возвращает DWORD (идентификатор потока Windows), возвращаемый функцией Win32 getCurrentThreadId(), а не HANDLE (дескриптор потока Windows), возвращаемый функцией Win32 getCurrentThread().

person lpapp    schedule 04.12.2014
comment
Благодарю вас! Вы дали лучший ответ. Я уверен, что с моим кодом много проблем, но я все еще новичок. То, как вы предложили, сработало сейчас, я думаю, что moc делает что-то за кулисами и создает для меня проблемы. - person melanholly; 04.12.2014
comment
@melanholly: печать текущих тем приблизит нас к решению вашей основной причины. - person lpapp; 04.12.2014
comment
@melanholly: я думаю, что ваша настоящая проблема может быть это: The object cannot be moved if it has a parent. Вы не показали конструктор TwidoDevice, но я предполагаю, что вы установили родителя с одним из ваших аргументов. - person lpapp; 04.12.2014
comment
Нет, у меня нет проблем с переездом. Там все работает хорошо. Я имею в виду, что родитель настроен. - person melanholly; 04.12.2014
comment
@melanholly: в этом проблема. Это не должно быть установлено! - person lpapp; 04.12.2014
comment
@Ipapp Я знаю, что если я хочу переместить объект в поток, я не должен устанавливать родителя. Это именно то, что я делаю. - person melanholly; 04.12.2014
comment
@melanholly: Итак, мы проверили все, что, насколько я понимаю, может пойти не так, основываясь на вставленном коде, поэтому я предполагаю, что следующим будет предоставление SSCCE, который воспроизводит проблему, по крайней мере код конструктора TwidoDevice. На всякий случай, вы также распечатали текущие идентификаторы потоков, и они различаются? - person lpapp; 04.12.2014
comment
@Ipapp Хорошо, я действительно выяснил, в чем проблема, когда я использую Qt::AutoConnection. Я думаю, что я изменяю очень мало кода, и компилятор не компилирует все. Теперь, когда я не вижу ожидаемого результата, я просто удаляю весь скомпилированный каталог и перекомпилирую проект. Кажется, это решает проблему при подключении. - person melanholly; 06.12.2014
comment
@melanholly: тебе нужно перепройти мок, да. Рад, что теперь это работает. - person lpapp; 06.12.2014

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

Однако лучший способ, как указывает lpapp, — это позволить Qt решить, что лучше, и просто использовать Qt::AutoConnection, что является значением по умолчанию. Он будет использовать QueuedConnection, если сигнальные и принимающие потоки различаются, и DirectConnection в противном случае.

person MatthiasB    schedule 04.12.2014
comment
Было бы даже лучше использовать auto (по умолчанию), чтобы быть справедливым. - person lpapp; 04.12.2014
comment
Тип подключения по умолчанию (автоматический) мне не подходит. Я не знаю почему, но когда я не передаю этот параметр, событие не фиксируется. - person melanholly; 04.12.2014
comment
@melanholly: я думаю, что главный вопрос в этой теме, почему это не работает. Можете ли вы предоставить SSCCE? - person lpapp; 04.12.2014
comment
@MatthiasB Ну, я отлично работал с типом QueuedConnection. Благодарю вас! - person melanholly; 04.12.2014
comment
@ipapp Что такое SSCCE и как я могу его предоставить. - person melanholly; 04.12.2014
comment
@melanholly: я отлично работал с типом QueuedConnection -> ИМХО, вы просто скрываете некоторые проблемы, которые могут возникнуть позже. Вам нужно понимать, что делает ваш код, а не оставлять вещи, которые вроде бы работают, но вы не понимаете почему. По крайней мере, я бы не назвал это надежным программным обеспечением. SSCCE означает предоставить небольшой пример, который воспроизводит проблему для нас. Во-первых, не могли бы вы распечатать текущие потоки, где вы подключаетесь, а также в слоте, который вызывается? Они одинаковые или разные? - person lpapp; 04.12.2014