Немного повторюсь:" данная реализация не претендует на абсолютно правильную, это скорее описание, одного из вариантов, как можно сделать."
Я считаю, что не ошибается только тот, кто ничего не делает.
Эта статья появилась, потому что было желание рассказать и показать, как можно сделать. Возможно в будущем она кому-то пригодится, может кто-то найдет в ней что-то полезное для себя, какие-то идеи или возможности в реализации своей задумке.
Она не ставит своей целью научить кого-то, как надо программировать. Но, я лично считаю, что чем больше подобных статей будет на русском языке, тем больше у разработчиков будет информации о той или иной технологии, тем легче им будет работать.
В конце концов, по мере обсуждения всплыли кое-какие подробности реализации, лично которых я не знал. Кто-то прочитает, взвесит для себя все за и против, и сделает выбор в сторону того или иного способа работы.
Если это будет действительно так, значит я не зря потратил свое время на написание данной статьи, а ваше время на ее прочтение.
Можно у вас попросить пример программы(а лучше статью) полного использования прелестей Qt в стиле Qt?
Я думаю мне, да и не только мне, будет полезно поучиться у вас, как надо использовать Qt.
Если придет больше (с чем сталкиваться еще не приходилось ни разу, но вдруг) ничего страшного. При разборе пакета этот нюанс учтен, вместе с пакетом передается его длина и контрольная сумма.
Вместо двойной буферизации можно использовать кольцевой буфер, тогда мы ничего не пропустим.
Добавлю еще, способ, приведенный выше не подходит по двум причинам, во-первых, нужно всегда знать длину пакета. Во-вторых, если устройство не отвечает на запрос, мы об этом не узнаем, будем крутиться в потоке, в ожидании пакета.
Отличные ссылки, спасибо! Как-то прошли мимо меня.
Если вы не против, добавлю их в статью.
Тот пример, который привели вы, немного отличается от того, что привел я, хотя бы потому, что в вашем примере ожидается, что слот будет обрабатываться в том же потоке, что и сигнал. А это не так. Это вводит в заблуждение.
В моем же примере вся работа по отправке и приему будет проходить в одном потоке, а вот обработка сигналов будет в другом потоке.
Если подытожить, то в двух словах можно сказать так, все, что вы поместите в метод run(), будет выполняться в этом потоке, во всех остальных случаях в основном потоке, за исключением случая, когда присоединение слотов и сигналов происходит в методе run().
Отвечу сам себе, вы абсолютно правы, в том контексте, который привели вы, блокировка не нужна. Не сразу понял о какой части кода идет речь. Прошу прощения и посыпаю голову пеплом.
Метод transaction вызывается из основного потока.
Пояснение: «Note, the transaction() method is called in the main thread, but the request is provided in the MasterThread thread. The MasterThread data members are read and written concurrently in different threads, thus the QMutex class is used to synchronize the access.»
И далее: «The transaction() method stores the serial port name, timeout and request data. The mutex can be locked with QMutexLocker to protect this data. The thread can be started then, unless it is already running.»
Для уверенности, что в процессе инициализации значения переменных не будут изменены извне. Возможно это лишнее, такие действия основываются на тех примерах, которые идут с Qt.
При открытии COM-порта это будет лишнее, ИМХО.
Инициализацию ресурсов мне кажется не очень правильно делать в одном потоке, а использование в другом, кто даст гарантию в таком случае, что на середине передачи значения, в массиве, который передается, не изменятся из другого потока?!
Не получится ли так, что мне нужно передать последовательность байт (0x20, 0x45, 0x12, 0x17), но в момент передачи значение третьего байта внешний поток поменяет на 0x14, и у меня в COM-порт уйдет (0x20, 0x45, 0x14, 0x17)?
Тут есть один нюанс, дело в том, что Windows не система реального времени. А это значит, что посылаете вы допустим с устройства 18 байт. В COM-порт он попадают побайтно, сама система их может выдать сразу 18, или сначала 10, потом 8. Или сначала 12, потом 6.
Что мы получим в этом случае, принимаем сигнал readyRead, вызываем readAll и получаем 12 байт вместо 18. Потому что в момент прихода сигнала в буфер успело попасть только 12 байт, еще 6 придут чуть позже. Проверено не единожды с использованием разных контроллеров и программы AdvansedSerialPort, запущенной в режиме сниффера.
Я не совсем уверен что в данном случае есть прямая зависимость от логики в гуёвом потоке и стабильностью работы. Может приведете пример, что мне надо изменить, чтобы программа упала?
Спасибо за статью, как-то прошла мимо меня, но в ней опять же не раскрывается тема, почему наследование от QThread — это плохо и к чему оно может привести.
Было бы интересно уточнить именно этот момент, чтоб окончательно поставить все точки на i.
Здесь я склонен с вами не согласится. Есть два подхода, я об этом упомянул в тексте статьи.
Можно помещать объект в поток, используя QObject::moveToThread, а можно наследоваться от QThread.
Сами разработчики-Qt используют их оба в своей документации и примерах.
Я в своей работе использовал и тот и другой подход. У каждого есть свои плюсы и минусы.
Если не сложно, хотелось бы увидеть какие-либо реальные примеры, показывающие, что использование наследования от QThread — это плохо.
А почему он должен подвисать?!
Наиболее времязатратное действие происходит в другом потоке, влияние на GUI быть не должно. Во всяком случае данный подход какого-либо заметного влияния на работу GUI не оказывает.
Я с вами вполне согласен. Тот пример, что привел я, проще реализовать, используя MVC. Когда я брался за свой проект, взвесив все за и против MVC и MVP, выбрал MVP. Реальный проект сложнее, чем то, который приведен в примере, поэтому явно, почему был выбран именно MVP, возможно не видно. Но и цели такой не было, была цель показать подход.
Я считаю, что не ошибается только тот, кто ничего не делает.
Эта статья появилась, потому что было желание рассказать и показать, как можно сделать. Возможно в будущем она кому-то пригодится, может кто-то найдет в ней что-то полезное для себя, какие-то идеи или возможности в реализации своей задумке.
Она не ставит своей целью научить кого-то, как надо программировать. Но, я лично считаю, что чем больше подобных статей будет на русском языке, тем больше у разработчиков будет информации о той или иной технологии, тем легче им будет работать.
В конце концов, по мере обсуждения всплыли кое-какие подробности реализации, лично которых я не знал. Кто-то прочитает, взвесит для себя все за и против, и сделает выбор в сторону того или иного способа работы.
Если это будет действительно так, значит я не зря потратил свое время на написание данной статьи, а ваше время на ее прочтение.
Я думаю мне, да и не только мне, будет полезно поучиться у вас, как надо использовать Qt.
Вместо двойной буферизации можно использовать кольцевой буфер, тогда мы ничего не пропустим.
Если вы не против, добавлю их в статью.
Тот пример, который привели вы, немного отличается от того, что привел я, хотя бы потому, что в вашем примере ожидается, что слот будет обрабатываться в том же потоке, что и сигнал. А это не так. Это вводит в заблуждение.
В моем же примере вся работа по отправке и приему будет проходить в одном потоке, а вот обработка сигналов будет в другом потоке.
Если подытожить, то в двух словах можно сказать так, все, что вы поместите в метод run(), будет выполняться в этом потоке, во всех остальных случаях в основном потоке, за исключением случая, когда присоединение слотов и сигналов происходит в методе run().
Пример:
Метод transaction вызывается из основного потока.
Пояснение:
«Note, the transaction() method is called in the main thread, but the request is provided in the MasterThread thread. The MasterThread data members are read and written concurrently in different threads, thus the QMutex class is used to synchronize the access.»
И далее:
«The transaction() method stores the serial port name, timeout and request data. The mutex can be locked with QMutexLocker to protect this data. The thread can be started then, unless it is already running.»
Возможно я чего-то не так понял?
Надо будет протестировать на реальном железе.
При открытии COM-порта это будет лишнее, ИМХО.
Инициализацию ресурсов мне кажется не очень правильно делать в одном потоке, а использование в другом, кто даст гарантию в таком случае, что на середине передачи значения, в массиве, который передается, не изменятся из другого потока?!
Не получится ли так, что мне нужно передать последовательность байт (0x20, 0x45, 0x12, 0x17), но в момент передачи значение третьего байта внешний поток поменяет на 0x14, и у меня в COM-порт уйдет (0x20, 0x45, 0x14, 0x17)?
Что мы получим в этом случае, принимаем сигнал readyRead, вызываем readAll и получаем 12 байт вместо 18. Потому что в момент прихода сигнала в буфер успело попасть только 12 байт, еще 6 придут чуть позже. Проверено не единожды с использованием разных контроллеров и программы AdvansedSerialPort, запущенной в режиме сниффера.
Было бы интересно уточнить именно этот момент, чтоб окончательно поставить все точки на i.
Можно помещать объект в поток, используя QObject::moveToThread, а можно наследоваться от QThread.
Сами разработчики-Qt используют их оба в своей документации и примерах.
Я в своей работе использовал и тот и другой подход. У каждого есть свои плюсы и минусы.
Если не сложно, хотелось бы увидеть какие-либо реальные примеры, показывающие, что использование наследования от QThread — это плохо.
Наиболее времязатратное действие происходит в другом потоке, влияние на GUI быть не должно. Во всяком случае данный подход какого-либо заметного влияния на работу GUI не оказывает.