Извините за длину этого поста. Но я застрял на два дня …
Я работаю над приложением Qt 4.6 для Windows, которое связывается с аппаратным устройством через ActiveX.
Когда я посылаю команду, устройство выполняет какие-то действия, а когда оно выполнено (может занять до одной минуты), оно издает сигнал. Мне нужно дождаться этого сигнала, чтобы узнать, все ли прошло хорошо (или нет), и выполнить некоторые действия в последствии.
Команда отправляется на устройство, когда пользователь нажимает кнопку. И, очевидно, я не хочу, чтобы HMI зависал.
Я убежден, что я должен использовать темы. Итак, я определил три темы:
Вот диаграмма классов:
И диаграмма последовательности, чтобы показать, как я вижу вещи:
Пояснения:
Когда пользователь запускает мое приложение, HMI создается. Конструктор HMI вызывает конструктор Worker. Он конструирует аппаратный QAxObject. Затем он создает HardwareListener с указанием ссылки: QAxObject, QMutex и QWaitCondition. Затем конструктор Worker перемещает объект HardwareListener в другой поток и запускает его. Наконец, конструктор HMI запускает поток Worker.
Затем, когда пользователь нажимает кнопку, HMI посылает сигнал работнику. Рабочий отправляет команду оборудованию (эта команда может заблокировать поток на несколько секунд, поэтому мне нужен HardwareListener в другом потоке, чтобы не пропустить сигнал). Затем работник ожидает QWaitCondition (после блокировки QMutex).
После этого аппаратное устройство отправляет сигнал в HardwareListener, который активирует QWaitCondition. Поэтому рабочий поток прекращает ждать и завершает свои действия. Наконец, работник информирует HMI.
Проблема:
Потоки Worker и HardwareListener не создаются / не запускаются. Все сделано в основном потоке, так что, очевидно, это не работает. Я не обмениваюсь никакими специальными объектами между потоками (поэтому нет необходимости qRegisterMetaType()
)
Вопрос:
Мой дизайн приемлем? Могут быть и другие способы, но, как мне кажется, это самый простой способ (учитывая сложность).
РЕДАКТИРОВАТЬ:
Я изменил свой код, чтобы удалить QThread
наследование. Я использую moveToThread()
метод вместо.
Теперь темы работают нормально. ТЕМ НЕ МЕНИЕ У меня ошибка ActiveX: QAxBase: Error calling IDispatch member NewProject: Unknown error
,
Кажется, взаимодействие с оборудованием нарушено … Есть идеи?
You cannot move a QAxObject to another thread once it has been created.
РЕШЕНИЕ:
Наследование от QThread — это не хороший дизайн. Если работа, которую вы выполняете, требует больших вычислительных ресурсов, я бы рекомендовал использовать QThreadPool. Я не лучше использовать асинхронный дизайн. Это означает только вызов функции, которая никогда не блокирует и вместо этого подключается к сигналам, уведомляющим вас о том, что что-то произошло.
Так, например, отправка команды аппаратному обеспечению и выдача сигнала, когда аппаратное обеспечение выполнено. Если аппаратный API не предоставляет асинхронные функции, то вы застряли с использованием потоков.
QtConcurrentRun может помочь с этим. Обычно вам не нужно трогать темы самостоятельно; и чертовски намного проще без.
Других решений пока нет …