coroutines in C
- Войдите на сайт для отправки комментариев
Чт, 31/01/2019 - 21:46
Давно хотел обсудить с кем-нибудь, кому интересно, такую вещь, как protothreads, они же "coroutines in C".
https://ru.wikipedia.org/wiki/Протопотоки
http://dunkels.com/adam/pt/index.html
Я в курсе, что мнения разнятся от восторгов до "мерзкие, мерзкие макросы, фуу...".
Сразу скажу, я осмелился сделать на protothreads пару проектов, от простых к.автоматов управления питанием более умного устройства, до контроллера с Modbus-мастером, UDP и "динамическим" HTTP сервером. Это были только 51-е контроллеры. Всё успешно работает.
Вы или крестик снимите или трусы оденьте. С одной стороны процессор, с другой стороны ПЛИС, а вы как гордый сокол между ними.
Выглядит это примернр так (пример с сайта Адама Данкелса):
Полное отсутствие delay, явное отсутствие вызовов millis, явное присутствие ожидания события, но на время ожидания управление отдаётся... Кому?... Тем, кто не любит много делать в прерываниях (т.е. на "том свете"), а любит всё делать "в лоб".
И тем, кому надоели явные конечные автоматы.
Ещё раз, пожалуйста. Я слово verilog знаю конечно, но вы мне льстите, я может пару раз пальцем по ПЛИС провёл в своей жизни, но не то чтобы программировать. 51 - это такая забытая архитектура, как Тадж Махал, или пирамиды.
Трусы - надевают, замечу.
Давно хотел обсудить
Так обсуждайте! Почитаем Ваше мнение, посмотрим, может чего тоже скажем. А то пока это лишь приглашение кому-то начать обсуждение. Но идея-то Ваша, Вам и начинать.
А я вот, хоть убей, не понимаю смысла полноценной event-driven архитектуры на МК. В принципе понятно, когда система в idle state крутится - тогда, видимо, всё это красиво работает. Но при какой-то более-менее существенной нагрузке сразу встает проблема приоритерезации какого-то потока: читаем, допустим, буфер UART и тут прилетело требование обслужить входящий TCP коннект. Как тут без дополнительных наворотов придать системе способность выбрать наиболее важный процесс? Стоит ли оверхед выделки, как грицца или это просто ради челленджа на 100% использование ресурса МК (сделай, типа, как на Big PC)?
Так обсуждайте! Почитаем Ваше мнение, посмотрим, может чего тоже скажем. А то пока это лишь приглашение кому-то начать обсуждение. Но идея-то Ваша, Вам и начинать.
Я-то давно сам с собой обсудил, иначе не стал бы использовать. Коллеги смотрели на меня с интересом и переспрашивали "прото-кто?" или "что threads?".
Понимаю, что надо бы что-то вкусное на затравку, вот во 2-м сообщении немножко пока. Свои коды не могу выкладывать, не моя собственность.
Вот verilog и есть ваши протопотоки. Напишите компилятор verilog в процессоры и ваша тема исчерпана.
sadman41, здесь чисто кооперативная многозадачность. Точнее здесь нет никакой многозадачности. По сути - это конечный автомат, но программист пишет как будто кооператив. В этом и прелесть. А как реализовано. Почитайте вот, откуда ноги выросли:
https://ru.wikipedia.org/wiki/Устройство_Даффа
Мне нравятся такие изящные фиговины, например.
Вот verilog и есть ваши протопотоки. Напишите компилятор verilog в процессоры и ваша тема исчерпана.
Чем больше вы мне пишете - тем меньше я вас понимаю.
Нет, ну если просто нравятся изящные фиговины, то тут и обсуждать нечего. Кому-то нравится водка, кому-то пиво. И никакого консенсуса тут не будет. А вот собутыльников найти можно, теоретически.
Нет, ну если просто нравятся изящные фиговины, то тут и обсуждать нечего. Кому-то нравится водка, кому-то пиво. И никакого консенсуса тут не будет. А вот собутыльников найти можно, теоретически.
Терпеть не могу "изящных фиговин". Я не так выразился. Мне нравятся красивые решения некрасивых задач. Под "некрасивыми решениями" я понимаю конечные автоматы вида switch-case или ни дай бог if-else-if.
Если вы писали код под RTOS - вы поймёте о чём я. Здесь вам предлагается удобство кооперативной ОС без накладных расходов операционной системы. Здесь нет планировщика. Но здесь есть:
- передача управления (yeld),
- неблокирующее ожидание события (wait),
и ещё пара плюшек.
Полное отсутствие delay, явное отсутствие вызовов millis, явное присутствие ожидания события, но на время ожидания управление отдаётся... Кому?... Тем, кто не любит много делать в прерываниях (т.е. на "том свете"), а любит всё делать "в лоб".
Думаю, что дело пойдет лучше, если Вы предложите не код прототипа, а что-то более приземленное и более связанное с ардуинством. Чтение показаний с сенсоров, реакцию на эти показания... всё это в раскладе для протопотоков этих.
Видео:Я даже воздухом не дышу https://www.youtube.com/watch?v=7TsQLGimB2I
Все на чем люди пишут это языки высокого уровня. А вот то что уже заливается в камни ну очень далеко от оригинала. Особенно это ясно в ПЛИСах. Ну сложно набить большую таблицу. Вот и создают "многозадачные модули" и потом собирают из них "программу". Вы тоже подобное предложили в этой теме. А дальше просто написать компилятор и вуаля самая быстрая программа в итоге. Но Си все же не подходит для этого. Граната не той системы.
Да, думаю вы правы. Blink, например. Плохой из меня коммивояжёр)
Видео:Я даже воздухом не дышу https://www.youtube.com/watch?v=7TsQLGimB2I
Все на чем люди пишут это языки высокого уровня. А вот то что уже заливается в камни ну очень далеко от оригинала. Особенно это ясно в ПЛИСах. Ну сложно набить большую таблицу. Вот и создают "многозадачные модули" и потом собирают из них "программу". Вы тоже подобное предложили в этой теме. А дальше просто написать компилятор и вуаля самая быстрая программа в итоге. Но Си все же не подходит для этого. Граната не той системы.
Мы с вами точно в тех плоскостях, которые не пересекаются.
Да, думаю вы правы. Blink, например. Плохой из меня коммивояжёр)
Ради блинка потоки заводить... Нет, я, конечно могу себе представить, что в одном потоке идет отрисовка на дисплей, во втором чтение далласа, в третьем - bmp какого-нибудь. Но я, если честно, не совсем понимаю - в чем будет выигрыш против простого разноса по функциям и вызова их по-очереди в лупе.
Да, наверное, это было бы полезно со всякими далласами и пр. штуками у которых долгая конверсия. Но что-то я не совсем пойму, как в середине функции чтения отдать управление кому-то, а потом забрать через 500мс, а не через полчаса, когда какая-то функция соизволит его отдать.
Я не профессиональный кодо-пейсатель, поэтому не в курсе всех этих новомодных и старомодныхтенденций. Ну, на перловке многопоточного демона писал разок. Однако там всё рулится "само" - на уровне OS. Там главное отфоркнуться правильно, а дальше уже как и в однопоточном приложении работаешь.
[ Однако там всё рулится "само" - на уровне OS. Там главное отфоркнуться правильно, а дальше уже как и в однопоточном приложении работаешь.
Ну вот, набросал скетч, который мигает независимо двумя диодами. Там ещё serial независимо шлёт, но я его закоментил, тк второй светодиод - это RxD, и serial его периодически гасит (у меня Nano). А так все три потока работают вполне себе независимо, если сериал раскомментить, то и он будет работать.
Заголовочные файлы, если захочется проверить, лежат тут (распаковать в папку со скетчем lc.h, lc-switch.h, pt.h):
http://dunkels.com/adam/download/pt-1.4.tar.gz
Да, если подождать немного, то можно увидеть, что светодиоды рассинхронизируются, т.е. совсем всё независимо)
Скомпилил, глянул в build preproc. Сильно вникнуть не пытался, но да, действительно - похоже на скрытый за макросами псевдодиспетчер на свичах. Пока чувства смешанные. С одной стороны - весело оформить код так, чтобы никто нихрена не понял, а с другой - в итоге по коду раскиданы те же самые свичи/миллисы, только еще придется через полгода почесать репу чтобы вспомнить на кой ты так накрутил. Ну и, конечно, полномасштабное использование глобалов для взаимодействия тредов - краеугольный камень всей этой каши из топора. Боюсь, что в сколько-нибудь большом прожекте это осложнит жись, а вовсе не облегчит.
Однако, идея неплоха для незамысловатых алгоритмов с несвязными тредами: типа этот датчик читается раз в минуту, тот - раз в пять минут, дисплей обновляет данные каждую секунду. При требовании выполнения последовательных операций всё равно скатимся к банальному свичу State machine.
Такое вобщем... нишевое решение, на первый взгляд.
И скока размер получился для нано?
так оно же само и говорит - что только Глобалы учитывает:
Но я согласен, что это совсем не то, что хотелось бы видеть. Ипользует библиотека внутри класса буфер на 1кбайт - а ты и знать не будешь.
Ну, это да. Однако что-то мне визуально помнится, что корректней считалось )) Мож приснилось.
Вот, к слову, близкий по духу блинкер (индусский код по примеру того, что сгенерировался в preproc):
В принципе, накладные не так и велики. Если конечно, они не будут расти пропорционально коду.
Например, для RTOS пишут, что накладные расходы на Уно-Нано "всего" порядка 2к. Но они отжираются сразу и почти не зависят от пользовательского кода, насколько я знаю.
В принципе, накладные не так и велики. Если конечно, они не будут расти пропорционально коду.
Да, но и явного преимущества в этой технологии пока что не видно. Внутре те же самые свичи-миллисы.
Например, для RTOS пишут, что накладные расходы на Уно-Нано "всего" порядка 2к. Но они отжираются сразу и почти не зависят от пользовательского кода, насколько я знаю.
А чего потом делать с этой RTOS, если она всю память сожрет сразу? Вот мне нужен буфер для sprintf(), к примеру - откедова под него память получить?
Небольшая корректировка. Я же код Шварца с сериалом откомпилил, а свой без него. Сообразил, пока бродил к чайнику...
Итого, с прототредами:
Расчет RAM на совести линкера.
Для всяких далласов и прочих "долгих ожиданийЭ, мне гораздо больше нравится подход T++. Там отложенные значения естественны и предельно просты.
Если интересно, могу попросить у разработчиков реализацию, чтобы посмотреть насколько там малой кровью можно к wiring присобачить
Вот, к слову, близкий по духу блинкер (индусский код по примеру того, что сгенерировался в preproc):
Так что пока даже и не знаю - хорош ли этот тредогенератор... стоит ли изводить на него ресурс МК...
Вот-вот, вопрос в крови. Если для чтения далласа "без задержек" надо будет еще восемь страниц крепко заваренного на зубодробительной теории кода накидать, то среднестатистическая "ардуинная" задача превратится в неподъемный камень для целевой аудитории. Конечно есть люди, которые на двадцати разновидностях ассемблера пять лет будут полировать "погодную станцию" и выберут наилучший вариант в итоге, но таких тут единицы. Для большинства местных задачек и delay(1000), вобщем-то, не помеха - прямо скажем )) А уж "блинк без дилэй" закрывает 90% вопросов.
А ежели мы в него рельсу сунем, да? Как будто кто-то сомневается, что можно придумать условия, в которых одно решение будет превосходным, а другое - отвратным. Вы написали блинкер, который мыргает и я написал такой же. Чем они далеки друг от друга?
А в том, что разросшийся код со временем поглощает мозг своего создателя без остатка - у меня и сомнений нет. Хоть на if() он сделан, хоть на goto, хоть на тредах. Я же не пишу, что ваши прототреды отвратны. Да, как синтаксический сахар для людей, которым нравятся такие приемы и досконально понимающих, как развернутся данные макросы - они отличны. Конечно можно поиметь проблемы со вложенными в тред своими свичами, помучаться с глобалами и пр. Несомненно, что потр... атив определенное время и усилия на освоение бензопилы, можно ею создавать офигенные скульптуры. Но, повторюсь, что на мой взгляд это удел единиц, у которых этого времени в избытке или вот так сложилось, что мозг заточен под художества.
Вы написали блинкер, который мыргает и я написал такой же. Чем они далеки друг от друга?
...
Конечно можно поиметь проблемы со вложенными в тред своими свичами, помучаться с глобалами и пр.
Мой легко читается, и можно добавлять "потоки" относительно безболезненно. Он не выглядит "индусским".
Вы правы, чтобы пользоваться switch внутри прототредов надо понимать как они внутри устроены, в некоторых случаях просто нельзя.
Глобальные переменные - да, неприятно, но в С без них вообще сложно обойтись.
Дак мы щас квона попросим - он класс накидает и можно эти мигалки тоже безболезненно размножать ))
В конце концов - вы же хотели обсудить, а не найти тех, кто тоже восхищается данным решением. Вот мы и обсуждаем. Я излагаю мнения с дилетантской, пользовательской даже, точки зрения. Ардуина для меня - просто интересный квест, который иногда и в хозяйстве полезен. А интересный квест не должен быть таким, что игрок дальше первой загадки годами не может пройти )) И да - я часто пересматриваю свои программные подходы, не цепляюсь за старые. Но только в том случае, когда новое будет явно эффективней (субъективная категоря, не спорю) старого.
Глобальные переменные - да, неприятно, но в С без них вообще сложно обойтись.
Да ну?? ;))
Дак мы щас квона попросим - он класс накидает и можно эти мигалки тоже безболезненно размножать ))
В конце концов - вы же хотели обсудить, а не найти тех, кто тоже восхищается данным решением. Вот мы и обсуждаем. Я излагаю мнения с дилетантской, пользовательской даже, точки зрения. Ардуина для меня - просто интересный квест, который иногда и в хозяйстве полезен. А интересный квест не должен быть таким, что игрок дальше первой загадки годами не может пройти )) И да - я часто пересматриваю свои программные подходы, не цепляюсь за старые. Но только в том случае, когда новое будет явно эффективней (субъективная категоря, не спорю) старого.
Мигалка - это для примера, вы ж сказали что-то ардуинское для примера нужно. Возможности куда шире, насколько фантазии хватит. Несколько примеров использования есть на сайте автора. Он вроде даже с uIP прототреды использовал (кстати он также автор uIP и lwIP).
Я к ардуино так же отношусь примерно, вот и подумал, что этот форум - самое место обсуждать подобные неоднозначные подходы.
Да ну?? ;))
Ну да) Как из прерывание что-нибудь достать без глобальных? Мы же про С говорим?
Есть такая добрая традиция: каждый новичек в Ардуино немедленно создает "Общую теорию всего", в смысле прививает многопоточность к и без того замученной тушке АВР программирования.
Раньше было ясно, октуда ноги растут - новички приходили из Веб и жить не могли без объектного мышления и параллельный тредов.
Но тут новый человек говорит, что сам из эмбеддинга пришел и контроллеры на завтрак в тостере готовит, но что мы видим: опять треды!
Это не наезд, не принимай на свой счет, просто подшучиваю!
Мы, понимаешь, "...давно тут сидим" (с), видали много разных людей. Так вот есть две "полезности" в любой технологии програмирования:
1. Полезность для программистов - сокращение времени разработки, уменьшение вероятности ошибки, оптимизация кода;
2. Полезность для новичка - когда новичек, увидив новую для себя технологию, закричит: "Так вот из чего, на самом деле, тетька сделана!" и станет задавать меньше тупых вопросов на форуме.
-----
Полезностью для программиста разные технологии псевдотредов обладают лишь с точки зрения расширения эрудиции. Большинство тут - дядьки около 50 с такой эрудицией, что если ее расширить, она лопнет. Разрабатывать код, ограничивая себя одним инструментом - неудобно. Где-то в коде автоматная часть, где-то псевдопараллельная, где-то очередь, а где-то вообще делей() стоит!
Полезность для новичка у любой из показанных на моей памяти "технологий" - нулевая. Новичек просто не поймет, где и как можно, а где нельзя ее применять.
---------
Про эти конкретные псевдотреды - ничем не хуже других, вполне мило. Взял бы себе на вооружение? - ни за что! ;)) Просто потому, что для двух-трех потоков не нужно, а код с бОльшим количеством псевдопараллельных потоков, на мой взгляд, требует перепроектирования.
Где нужно? Разработка на скорость - например "на заказ". Думать о слиянии потоков и правильном проектировании кода - нет времени, если хватает ресурсов - быстро накидать решение, прогнать пару тестов и отдать заказчику.
Чё-тут-думать? ;)) - поток на кнопки, поток на экран, поток на сенсоры, поток на актуаторы. Вот и принцип ЛЮБОЙ программы для контроллера. Если есть Веб и сеть, то еще поток на Веб и поток на SMS-GSM. (это был сарказм)
Да ну?? ;))
Ну да) Как из прерывание что-нибудь достать без глобальных? Мы же про С говорим?
А писать интерфейсы начали только в С++? Или что-то мешает из прерывания дернуть функцию со статическими переменными? Вопрос желания.
Если не думать о правильном проектировании, то в итоге можно актуатором по лбу получить )) Расслабишься с этими тредами, потеряешь видение целостной картины таймлайна и приехали - всё какбутто красиво в каждом треде и научно правильно - с yeld-ами, но отчего-то на "одноядерном" контроллере с круговым переключением тредов не попадаешь сразу в нужный, а пропускаешь вперед себя кого-то, а у него там свои дела и твой йелд превращается в неопределённую бесконечность ))
Всё вы правильно пишете, господа, конечно это всё от скуки и однообразия. Периодически хочется что-то ещё попробовать.
А писать интерфейсы начали только в С++?
Опять я что-то пропустил? А можно ссылку, где почитать про это?
Если не думать о правильном проектировании
то в любом случае по лбу сработает.
Периодически хочется что-то ещё попробовать.
А писать интерфейсы начали только в С++?
Опять я что-то пропустил? А можно ссылку, где почитать про это?
Да ничего ты не пропустил. Просто сама концепция интерфейса не следствие ООП, а идея программирования, которую часто используют в ООП и там она удобнее, из-за механизмов наследования, например.
Ничего не мешает спрятать переменную (массив, структуру) в статик переменных функции и обращаться к ней геттерами и сеттерами, как самый примитивный интерфейс. Так и любые идеи интерфеса: "печатаемый", "открываемый", и все, что в голову придет. Пишешь универсальную интерфейсную функцию, которой передается параметр и некие особенности текущего объекта, потом, если "чистый Си", то макросом или обёрткой переназываешь, скрывая лишние параметры.
Просто в ООП языках больше выразительных средств, для написания подобного. И читать код легче.
Периодически хочется что-то ещё попробовать.
Женя. Вот ты - "язычник", а ты представляешь или, возможно, обсуждал и знаешь, КАК конструкции Т++ реализовывать?
Я ж немного компиляторщик был в юности... это, по мне, почти полный П! Вот это "неготовое" значение порождает не результат, а код, который надо или пометить или положить в стек, поставить семафоры... гимор на гиморе, не даром первые реализации были полным г...!
То есть писать программисту - удобно, решать компьютеру... блин! Он же перегреется! ;)))
Ну, я не в курсе самых последних реализаций (хотя спросить есть у кого, если надо). Последний раз, когда я плотно имел с этой штукой дело, они делали её для кластерной архитектуры поверх MPI. Т.е. Т-функция сразу же отдавалась свободному узлу (на самом деле передавалась диспетчеру). Доступ к Т-значениюю был реализован через функцию-акцессор, а в ней уже стоял семафор. Т.е. как только встречается т-значение, для него создаётся некий объект, который знает о себе готов он или нет. Именно этому объекту отдаётся готовое значение по мере его появления и именно у него спрашивается, когда значение нужно использовать.
А удобство, это да. Вот запустил я любимый даллас, а сам продолжаю делать свои дела. Когда же значение реально понадобилось, посто использую его. Если оно уже готово - отлично, если нет, то в этом месте подождёт. При этом я вообще никак не парюсь о параллельности, синхронизации и т.п.
Идея-то на самом деле растёт из теории "частичных вычислений", там порылись ребята из ИПМ (может знаешь) - Андрей Климов, Лёша Лацис и вообще их семнар (т.н. "рефал-компания", там ещё Сергей Абрамов). Чья персонально идея была - не помню.
Да ничего ты не пропустил. Просто сама концепция интерфейса не следствие ООП, а идея программирования, которую часто используют в ООП и там она удобнее, из-за механизмов наследования, например.
Ничего не мешает спрятать переменную (массив, структуру) в статик переменных функции и обращаться к ней геттерами и сеттерами, как самый примитивный интерфейс. Так и любые идеи интерфеса: "печатаемый", "открываемый", и все, что в голову придет. Пишешь универсальную интерфейсную функцию, которой передается параметр и некие особенности текущего объекта, потом, если "чистый Си", то макросом или обёрткой переназываешь, скрывая лишние параметры.
Просто в ООП языках больше выразительных средств, для написания подобного. И читать код легче.
Идея понятна, и вполне симпатична, но сильно усложнит жизнь. Что делать, если прерывание и потребитель вызовут интерфейсную функцию одновременно? Реентерабельность не поможет, вроде.
PS Хотя нет, вопрос глупый. Делать то же, что и всегда - атомизировать доступ к критичным участкам.
Вот и попробуйте Т++. Ссылку я выше давал.
Я только в С++ начал врубаться (точнее в ООП), а вы мне Т++ уже советуете. Я сходил, конечно, туда. Прочитал два раза, понял что не понял, и ушёл.
Кстати, я тут вспомнил, что у нас gcc, а у него есть address labels. Вот для этого случая в исходном архиве есть файл lc-addrlabels.h, который если использовать вместо lc-switch.h даст возможность пользовать switch-case внутри протопотоков в полном объёме. Правда накладные расходы возрастут с 2-х байт на поток до 4-х.
у него есть address labels
О! Настоящие мужчины подтянулись!
зачем нам рыба, раз есть икра?
Да ничего ты не пропустил. Просто сама концепция интерфейса не следствие ООП, а идея программирования, которую часто используют в ООП и там она удобнее, из-за механизмов наследования, например.
Ничего не мешает спрятать переменную (массив, структуру) в статик переменных функции и обращаться к ней геттерами и сеттерами, как самый примитивный интерфейс. Так и любые идеи интерфеса: "печатаемый", "открываемый", и все, что в голову придет. Пишешь универсальную интерфейсную функцию, которой передается параметр и некие особенности текущего объекта, потом, если "чистый Си", то макросом или обёрткой переназываешь, скрывая лишние параметры.
Просто в ООП языках больше выразительных средств, для написания подобного. И читать код легче.
Посидел, почитал на эту тему, много думал, много искал, опять много думал. Есть даже целая книга http://www.planetpdf.com/codecuts/pdfs/ooc.pdf.
Ну вот глядя на это вот всё, применительно к контроллерам, пытаться заниматься инкапсуляцией (ни дай бог полиморфизмом и наследованием) на чистом Си - это ООП-дрочерство в чистом виде. У меня слёзы кровавые наворачиваются, когда я вижу такой изврат. Хотя надо понимать, это придумали ещё до С++, не от хорошей жизни. Поэтому, если я буду писать на С - буду писать в духе этого языка, а смогу думать на С++, а там возможности видятся большие - если мой код станет таким же по размеру или меньше, чем на С - тогда я и скажу, что три кита ООП + умный компилятор + умный программист = лучше ламера на ассемблерном фортране.