LINUX.ORG.RU

Работа с двумя потоками в qt

 ,


1

2

Привет. В общем нужно два потока. Один поток это главный GUI, второй поток это управление внешними датчиками по шине rs485 назовем его mythreadbalancer. Нужно как-то организовать связь между ними думаю о сигналах и слотах.

Мне вот не ясно как лучше организовать код. Получается у меня будут другие окна не только main из которых мне тоже нужно отсылать и принимать данные потока mythreadbalancer. Получается что каждое окно будет принимать и передавать данные в поток mythreadbalancer. Не произойдут ли какие коллизии или еще что, у меня не достаточно опыта чтобы ответить на эти вопросы.

Ответ на: комментарий от Liz812

а вам нужны все элементы вектора в каждом потоке? тогда действительно можно и шаред поинтер заюзать

SR_team ★★★★★
()
Ответ на: комментарий от Liz812

Лучше бы он тебе объяснил почему он протирает штаны в вашем ПТУ вместо того чтобы делом заниматься.

anonymous
()
Ответ на: комментарий от Liz812

Понятия не имею. Нужно проверять на кокретном железе в конкретном прототипе программы.

rumgot ★★★★★
()
Ответ на: комментарий от Liz812

Ровно такая же, как при использовании потоков - бесконечность.

unC0Rr ★★★★★
()
Ответ на: комментарий от rumgot

Не очень понял твою позицию, ты за отдельный поток для чтения/записи в последовательный порт или нет?

Да как угодно, только не в 1 поток, когда всё приложение блокируется намертво. Осталось только запилить это «как угодно». Можно нормально на потоках - хорошо. Можно сделать какой-нибудь готовый async - еще лучше. Тут, как правило, всё упирается в самоуверенного исполнителя, который делает трудновоспроизводимый дедлок на ровном месте.

crutch_master ★★★★★
()
Последнее исправление: crutch_master (всего исправлений: 4)
Ответ на: комментарий от Liz812

Кто мешает организовать обмен данными между потоками через разделяемый объект?

Кривые руки.

crutch_master ★★★★★
()
Ответ на: комментарий от crutch_master

Да ты чё, издеваешься? Я тут уже сколько писал!? Робота с портом через сигнал readyRead и собственно все. Какие тут еще вопросы могут быть?

rumgot ★★★★★
()
Ответ на: комментарий от rumgot

И как это будет выглядить? Как в этом примере?, где написано:

waitForReadyRead() - This function suspends operation in the calling thread until new data is available for reading.
...
Calling these functions from the main, GUI thread, may cause your user interface to freeze. Example:

Как ты будешь дёргать readyRead? Долбить по кнопке? А что внутри readyRead? Тред? Ой вей.

crutch_master ★★★★★
()
Последнее исправление: crutch_master (всего исправлений: 3)
Ответ на: комментарий от crutch_master

readyRead - это сигнал, он сам появится, не надо для этого ничего делать, и уж тем более блокировать поток.

Вон же, прямо в доках пример: https://code.qt.io/cgit/qt/qtserialport.git/tree/examples/serialport/creaderasync/serialportreader.cpp

unC0Rr ★★★★★
()
Ответ на: комментарий от unC0Rr

readyRead - это сигнал, он сам появится
сам
★★★★★
САМ

Я даже не знаю что сказать. Это я жс макака, а вы - плюсовики или нет?
САМ сигнал, решил и появился.

crutch_master ★★★★★
()
Последнее исправление: crutch_master (всего исправлений: 2)
Ответ на: комментарий от crutch_master

Сигнал будет сгенерирован объектом класса QSerialPort. И подключив свой слот к данному сигналу ты будешь читать в этом слоте.

rumgot ★★★★★
()
Ответ на: комментарий от rumgot

Нет. В этом же.

То есть, чтобы не заблокировать гуй тред будет сперва его рисовать, потом чекать собитыя, потом прочитает пачку байт из порта, потом опять пойдет рисовать? Зачем пихать это всё в 1 поток и усложнять себе жизнь?

crutch_master ★★★★★
()
Ответ на: комментарий от crutch_master

Тебе-то какая разница, что там внутри у класса QSerialPort? Да хоть отдельный процесс - факт, что тебе не нужно создавать потоки для работы.

unC0Rr ★★★★★
()
Ответ на: комментарий от rumgot

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

crutch_master ★★★★★
()
Ответ на: комментарий от unC0Rr

Да хоть отдельный процесс - факт, что тебе не нужно создавать потоки для работы.

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

crutch_master ★★★★★
()
Последнее исправление: crutch_master (всего исправлений: 1)

Для работы с окнами достаточно одного потока.
Никогда коллизий не было.
Для общения с переферией или долгой обработки используй другой поток.

Deleted
()
Ответ на: комментарий от crutch_master

Самое прикольное, что класс QSerialPort внутри тоже не запускает потоки, а использует QSocketNotifier под никсами (ему приходится запускать поток, да) и под виндой нативный асинхронный апи без всяких потоков. А тут предлагают вручную мутить потоки, ну бред же.

unC0Rr ★★★★★
()
Ответ на: комментарий от unC0Rr

А тут предлагают вручную мутить потоки, ну бред же.

Так рандомый кодер ноги себе так оторвёт с этими потоками, а потом все кто рядом оторвут ему еще и руки. Пока ты молодой кажется, что это просто, сейчас херак, херак и сделаю 2 потока, уже тред на лоре создал.

crutch_master ★★★★★
()
Ответ на: комментарий от Liz812

А если я скажу, что компьютер, на котором будет крутиться программа, будет одноядерным, как будешь выкручиваться?

unC0Rr ★★★★★
()
Ответ на: комментарий от unC0Rr

Да пофиг. Мне операционная система гарантирует что поток работы с портом все равно получит управление и я считаю данные вне зависимости от того как сильно загружен поток с GUI.

Liz812
()
Ответ на: комментарий от Liz812

Предположу, что все будет работать. А вот если действительно упрусь в недостаточную производительность (что будет видно на этапе прототипирования), тогда буду решать, применить ли дополнительные потоки.

rumgot ★★★★★
()
Ответ на: комментарий от Liz812

Если твой поток с Gui завис, то пофиг, что второй поток что сделал и прислал в первый поток результирующие данные - ведь первый поток будучи зависшим все равно ничего сделать с этими данными не сможет.

rumgot ★★★★★
()
Ответ на: комментарий от Liz812

операционная система гарантирует что поток работы с портом все равно получит управление

Раньше, чем GUI закончит свои делишки? Обоснуй.

unC0Rr ★★★★★
()
Последнее исправление: unC0Rr (всего исправлений: 1)
Ответ на: комментарий от crutch_master

Аля улю гони гусей. :)

Прочитал по диагонали весь тред, думал, что не буду ничо писать, ан нет, терпения не хватило слушать весь холивар. :)

  1. Qt - это событийно-ориентированный фреймворк. Где для каждого потока (что для главного потока QCoreApplication, что для QThread) имеется свой евент-луп. В этом евент-лупе крутится диспетчер (реализация которого отличается для разных ОС), который висит на WaitForMultipleObjects/select (грубо) и по срабатыванию оных мониторит те или иные события (как на дескрипторах у-в, сообщений от окон и прочее). Лезть сюда с std::thread как бы сомнительно, т.к. внутри у Qt все заточено именно на ее классы.

  2. QSerialPort - полностью «асинхронный» класс. Т.е. ввод/вывод (read/write) являются неблокирующими, и эти вызовы заверщаются «мгновенно». События о приходе данных или о завершении передачи мониторятся на дескрипторе устройства (как уже говорили выше) при помощи QSocketNotifier (это select/poll/epoll в диспетчере) на *nix, или QWinOverlappedIoNotifier на Windows (это IOCP). Далее, суть в том, что QSocketNotifier не создает никаких потоков, он регистрируется в диспетчере евент-лупа в том потоке, в котором создан (в главном, если не делали moveToThread) и «добавляет» свой дескриптор (ака дескриптор серийного порта) в диспетчер. И там уже диспетчер (на select/poll/epoll) мониторит события на все зареганные дескрипторы. Как только происходит что-то на каком-то дескрипторе, то диспетчер дергает соответствующий коллбек (посылает сигнал activated()) того объекта с которым ассоциирован дескриптор (в данном случае это конкретный QSocketNotifier). По этому сигналу от нотификатора внутри у QSerialPort выполняются соответствующие экшены (в зависимости от типа нотификатора.. если он типа Read, то QSerialPort вычитывает все что пришло в буфер и посылает readyRead()… если он типа Write, то посылает сигнал bytesWritten() и пытается передать следующую порцию данных).

В Windows ситуация похожая, но там для могиторинга событий сейчас используется (до Qt 5.14) класс QWinOverlappedIoNotifier который создает отдельный поток QThread для мониторинга IOCP событий (т.е. это все не в главном потоке выполняется). Остальныя логика такая же как и в случае с *nix.

  1. Все операции read/write пустяковые и не занимают никакого времени (неблокирующие): просто пачка данных из юзер спейса копируется в кернел спейс.. И по сути все.

Другое дело, что есть «нюансы»: например, на Windows если кликнуть на заголовок окна мышкой и удерживать ее, то весь цикл сообщений останавливается на время (или навсегда, в зависимости от того, левая это кнопка мышки или правая). В этом случае, все события в диспетчере евент-лупа не будут обработаны, пока пользователь не отпустит мышку. Для QSerialPort это может грозить переполнением FIFO драйвера и ошибкой overrun (чем дольше юзер держит - тем все больше и больше байт может быть принято в FIFO и не вычитано). Что грозит потерей данных. В *nix такой проблемы вроде не возникает, это только виндовая «фича».

Следовательно, дабы обезопасить себя от такого рода факторов (если большие скорости обмена и потеря данных критична), то желательно мувить QSerialPort в отдельный QThread.

ИМХО, использование std::thread в Qt это нонсенс, т.к. оно не вписывается в общую концепцию (для этого есть QThread, если сильно хочется).

Вот, в принципе и всЁ, думаю, мне не придется писать это снова и снова. А то, ругаются, тут, понимашь, как малые дети. :)

kuzulis ★★
()
Ответ на: комментарий от kuzulis

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

Другое дело, что есть «нюансы»: например, на Windows если кликнуть на заголовок окна мышкой и удерживать ее, то весь цикл сообщений останавливается на время

Вот тут я не согласен сразу добавлять поток на всякий случай. Тут либо сделать специальную реализацию чтения под винду с отдельным потоком либо не делать вообще в зависимости от используемого протокола обмена данными с устройством: например упомянутый протокол Modbus RTU - это запрос-ответ, соответственно если остановился цикл обработки событий, одно сообщение встало в очередь и следующий дальнейший запрос не отправляется - то все будет корректно после восстановления.

rumgot ★★★★★
()
Ответ на: комментарий от kuzulis

использование std::thread в Qt это нонсенс, т.к. оно не вписывается в общую концепцию

Ты фигню какую то написал. А если мне с сетью работать надо? Зачем использовать QThread, если есть решения на std::thread.

Liz812
()
Ответ на: комментарий от Liz812

Что ОС выделит потоку, читающему сериалпорт, время раньше, чем поток GUI смог бы закончить свои отрисовки и прочитал бы порт.

unC0Rr ★★★★★
()
Ответ на: комментарий от unC0Rr

А что потом будет?

Потом QWinOverlappedIoNotifier будет выкинут, и будет использоваться «alertable» ввод/вывод при помощи «Ex» ф-ций. :)

kuzulis ★★
()
Ответ на: комментарий от Liz812

А если мне с сетью работать надо?

QAbstractSocket.

Зачем использовать QThread, если есть решения на std::thread.

Потому что не надо лезть со своим уставом в чужой монастырь!

ЗЫ: Все болеее-менее значимые фреймворки имеют свои обертки над тредами и прочей лабудой, которые замечательно вписываются в их событийную (и не только событийную) модель.

Никто не запрещает использовать std::thread в своих консольных не-Qt тулинах. Но в Qt их использование не дает никаких плюшек. :)

kuzulis ★★
()
Последнее исправление: kuzulis (всего исправлений: 2)
Ответ на: комментарий от kuzulis

Предлагаешь городить свои костыли на QAbstractSocket? И вообще почему когда мне надо просто читать из порта я должна думать о event loop в окошках который тут никаками боком? Это пример очень кривого дизайна и он оскорбляет мои эстетически чувства.

Завтра я решу что Qt слишком тяжелый и сложный и возьму что-то другое. Мне все переписывать и отлаживать по новой?

Liz812
()
Ответ на: комментарий от Liz812

Кажется у вас проблемы с выбором профессии. Вы даже не догадываетесь о чем порой думает программист, например мой коллега сейчас думает, как по табличным данным произвести аппроксимацию и восстановить передаточную функцию обратной связи, а мой другой коллега разбирается в скинутом на него коде, при этом рассматривая электрическую схему прибора, а я вот сижу и пишу руководство пользователя, параллельно думая как бы это по удобнее запихнуть в программу, а я еще мы используем Qt - потому что это указано в ТЗ.

Silerus ★★★★
()
Ответ на: комментарий от Liz812

Это пример очень кривого дизайна и он оскорбляет мои эстетически чувства.

Тонко! ))))

ЗЫ: Заканчиваю пустой и неинтересный трЁп - мне надо работать: писать на ‘богомерзком’ Qt. ))))

kuzulis ★★
()
Последнее исправление: kuzulis (всего исправлений: 4)
Ответ на: комментарий от Silerus

Меня так учат, что разные сущности должны быть логически разделены. Иначе это называется «протечка абстракций». Qt пусть занимается исключительно GUI а если в своей программе вы его с легкостью не можете заменить на gnome, то у меня для вас очень плохие новости. У вас архитекторы не понимают как нужно проектировать ПО.

Liz812
()
Ответ на: комментарий от Liz812

Бросай профессию, тебя ждут БОЛЬШИЕ разочарования, либо учись в компромиссы, поверь практика, от того чему тебя учат очень далека и еще инженер-программист и программист - это абсолютно разные вещи. И еще если ты думаешь, что Gtk не такой же монстр, как Qt - я тебя сильно разочарую.

Silerus ★★★★
()
Последнее исправление: Silerus (всего исправлений: 1)
Ответ на: комментарий от crutch_master

Как в этом примере?

это пример для долбоебов. waitForReadyRead() не нужно вызывать никогда.

anonymous
()
Ответ на: комментарий от Liz812

В Qt все логически разделено. Например Widgets и Network - это разные модули и их сущности не зависят друг от друга. Можно использовать одно без другого. Кроме того можно использовать для сети совсем другую библиотеку и прочее. Тебе вроде бы говорят правильные вещи, но неверно их понимаешь.

rumgot ★★★★★
()
Ответ на: комментарий от Liz812

Предлагаешь городить свои костыли на QAbstractSocket?

Костыли городишь ты при самостоятельной реализации обмена данными между потоками.

rumgot ★★★★★
()
Ответ на: комментарий от Silerus

Их препод считает, что Qt кривой, сигналы/слоты - костыли. Тут преподу нужно мозги вправлять ибо забил детям головы наивной херней.

rumgot ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.