Python-сообщество
драйвера пишут на C потому что они на низком уровне связанны с ОС. Даже если на Питоне может быть физически и можно, но это делать никто не будет. Вы, точно не напишите, хотя бы потому что задаете такой вопрос. Возьмите драйвера официальные на модем, загляните в исходники, сделайте выводы.
Тема флейм.
_________________________________________________________________________________
полезный блог о python john16blog.blogspot.com
#3 Июль 6, 2014 09:09:40
Можно писать драйвера или нет
Я задал такой вопрос потому что все говорят что на питоне можно писать все.
Можно тогда написать флешь плеер как Adobe Flash Player. Или нет
Могут ли драйверы Windows быть написаны на Python?
Да. Вы не можете создавать «классические» драйверы режима ядра. Однако, начиная с XP, Windows предлагает «Рамка драйверов пользовательского режима» . Они не могут делать все, очевидно — любой драйвер, используемый при загрузке ОС, должен быть в ядре. Но с UMDF вам нужно только реализовать COM-компоненты.
Помимо драйверов загрузки, вы также не можете записывать драйверы UMDF, которые:
- Обработка прерываний
- Непосредственный доступ к оборудованию, например, прямой доступ к памяти (DMA)
- имеют строгие циклы синхронизации.
- Использовать невыгружаемый пул или другие ресурсы, зарезервированные для режима ядра.
Хороший способ получить представление о том, почему это практически невозможно, — это прочитать совет Microsoft по использованию С++ в драйверах. Как производная от C, использование С++ представляется простым. На практике это не так.
Например, вы должны решить для каждой функции (и действительно каждой инструкции сборки), будь то в доступной для страниц или нестранимой памяти. Для этого требуются расширения для C, тщательное использование новых возможностей С++ или в этом случае специальное расширение для языка Python и виртуальной машины. Кроме того, ваша совместимая с драйвером VM также должна иметь дело с разными IRQL — там есть иерархия уровней, которые ограничивают то, что вы можете и чего не можете сделать.
Могут ли драйверы Windows быть написаны на Python?
могут ли драйверы Windows быть написаны на Python?
7 ответов
да. Вы не можете создать «классические» драйверы режима ядра. Однако, начиная с XP, Windows предлагает Пользовательский Режим Driver Framework. Очевидно, они не могут делать все — любой драйвер, используемый при загрузке ОС, очевидно, должен быть в режиме ядра. Но с UMDF вам нужно только реализовать com-компоненты.
помимо драйверов времени загрузки, вы также не можете писать драйверы UMDF, которые:
- обрабатывать прерывания
- сразу оборудование доступа, как прямой доступ к памяти (DMA)
- имеют строгие временные циклы
- используйте невыгружаемый пул или другие ресурсы, зарезервированные для режима ядра
окончательный ответ не без встраивания интерпретатора в ваш в противном случае драйвер c/assembly. Если у кого-то есть рамки, то ответ нет. Как только у вас есть интерпретатор и привязки на месте, остальная часть логики может быть выполнена в Python.
однако написание драйверов является одной из вещей, для которых C лучше всего подходит. Я предполагаю, что полученный код Python будет выглядеть очень похоже на код C и победит цель интерпретатора накладные расходы.
хороший способ получить представление о том, почему это практически невозможно, — это прочитать совет Microsoft об использовании C++ в драйверах. Как производная от C, использование C++ кажется простым. На практике это не так.
например, вы должны решить для каждой функции (и действительно для каждой инструкции по сборке), находится ли она в подкачиваемой или не подкачиваемой памяти. Это требует расширения до C, тщательного использования новых функций C++ или в этом случае специального расширения для язык Python и виртуальная машина. Кроме того, ваша совместимая с драйверами виртуальная машина также должна иметь дело с различными IRQLs-существует иерархия «уровней», которые ограничивают то, что вы можете и не можете сделать.
Python работает на виртуальной машине, поэтому нет.
вы можете написать компилятор, который переводит код Python на машинный язык. Как только вы это сделаете, вы сможете это сделать.
Я не знаю ограничений на драйверы в windows (схемы выделения памяти, динамическая загрузка библиотек и все), но вы можете встроить интерпретатор python в свой драйвер, и в этот момент Вы можете делать все, что захотите. Не думаю, что это хорошая идея 🙂
никогда не говори никогда, Но да.. нет!—1—>
вы можете взломать что-то вместе, чтобы запустить пользовательские части драйверов в python. Но материал в режиме ядра может быть выполнен только в C или сборке.
нет они не могут. Драйверы Windows должны быть написаны на языке, который может
- интерфейс с API на основе C
- компиляции в машинный код
опять же, ничто не мешает вам написать компилятор, который переводит python в машинный код;)
Верно ли что python идеально подходит для написания драйверов устройств
помимо драйверов времени загрузки, вы также не можете писать драйверы UMDF, которые:
окончательный ответ не без встраивания интерпретатора в ваш в противном случае драйвер c/assembly. Если у кого-то есть рамки, то ответ нет. Как только у вас есть интерпретатор и привязки на месте, остальная часть логики может быть выполнена в Python.
однако написание драйверов является одной из вещей, для которых C лучше всего подходит. Я предполагаю, что полученный код Python будет выглядеть очень похоже на код C и победит цель интерпретатора накладные расходы.
например, вы должны решить для каждой функции (и действительно для каждой инструкции по сборке), находится ли она в подкачиваемой или не подкачиваемой памяти. Это требует расширения до C, тщательного использования новых функций C++ или в этом случае специального расширения для язык Python и виртуальная машина. Кроме того, ваша совместимая с драйверами виртуальная машина также должна иметь дело с различными IRQLs-существует иерархия «уровней», которые ограничивают то, что вы можете и не можете сделать.
Python работает на виртуальной машине, поэтому нет.
вы можете написать компилятор, который переводит код Python на машинный язык. Как только вы это сделаете, вы сможете это сделать.
Я не знаю ограничений на драйверы в windows (схемы выделения памяти, динамическая загрузка библиотек и все), но вы можете встроить интерпретатор python в свой драйвер, и в этот момент Вы можете делать все, что захотите. Не думаю, что это хорошая идея
никогда не говори никогда, Но да.. нет!—1—>
вы можете взломать что-то вместе, чтобы запустить пользовательские части драйверов в python. Но материал в режиме ядра может быть выполнен только в C или сборке.
нет они не могут. Драйверы Windows должны быть написаны на языке, который может
опять же, ничто не мешает вам написать компилятор, который переводит python в машинный код;)
Для каких задач подходит Python: разбираемся вместе с NASA и опытными разработчиками
Python называют универсальным языком программирования. Он действительно подходит почти для любых задач или это просто миф?

Python заслужил славу самого простого языка для входа в разработку — код на нём лаконичный и его легко читать. Мы решили разобраться, для каких задач он подходит и в какие проекты вписывается идеально, а на каких используется с ограничениями. Попутно расспросили бывалых питонистов, что они любят писать на Python, а что — не особо.
Невероятная популярность Python
«Я точно не собирался создавать язык, предназначенный для массового применения», — сказал как-то Гвидо ван Россум, создатель Python. В общем, он не специально Сегодня Python — один из самых популярных языков программирования. Например, он несколько раз становился языком года по версии TIOBE.

По количеству проектов на GitHub он тоже держит отличные позиции — в 2020 году разменял свой миллион: больше проектов только у JS. То есть и на GitHub это самый популярный язык программирования, если вы понимаете, о чём мы

Pinterest и Instagram были написаны на Python. В ЦРУ использовали Python для создания своего хакерского инструментария, в Google — для поиска по веб-страницам, в Pixar — для производства фильмов, в Spotify — в рекомендательной системе. А ещё на Python кодят NASA и их подрядчики.
И это вполне оправданный выбор — помимо лаконичности, качества кода и низкого порога входа, в Python есть ещё одна киллер-фича: библиотеки практически для всего на свете — от разработки игр до астрономии и расчёта генетических алгоритмов (тот же DEAP). Шутка ли — участники комьюнити уже загрузили в сеть более 145 тысяч библиотек. Такими темпами скоро можно будет не писать программы на Python и он станет no-code-инструментом Плюс Python может давать выигрыш в скорости создания программ по сравнению с другими языками в два или три раза.
Разберёмся, в каких направлениях и насколько успешно сегодня используют Python. Но мы же за визуальное сопровождение повествования, поэтому резюме по каждому направлению сделаем с помощью эмодзи. Вот наша объективнейшая система оценок:
— к чёрту ваш Python (спойлер: этот смайлик больше не появится в статье).

Пишет про технологии и бизнес.
Автоматизация и скрипты
Один из мифов о Python гласит, что это язык сценариев, а его конкуренты — Perl, Ruby, Bash, Zsh и Lua. Python и правда позволяет легко автоматизировать задачи и писать скрипты, да и файлы с Python-кодом часто называют сценариями, а не программами.

Михаил Корнеев
Тимлид в компании BestDoctor и автор YouTube-канала «Хитрый питон»
«Python — язык-клей, на котором можно быстро всё выстроить и объединить. Например, моему знакомому нужно было автоматизировать работу в Trello: ставить задачи, передавать статистику, строить графики, присылать напоминания при задержке сроков и так далее. Мы очень быстро нашли готовую Python-библиотеку для работы с Trello — и он выполнил эту задачу буквально за несколько дней».
Ещё программы на Python используют для управления компонентами других приложений — их подключают в контрольных точках, чтобы настроить продукт под конечного пользователя или выполнить какие-то рутинные операции, передать информацию с одного этапа на другой, то есть как своеобразный клей между большими блоками-кубиками.

Алексей Фирсов
Руководитель Python-практики в компании S7 TechLab
«Я играю в Factorio, там надо возить ресурсы с помощью поездов, управляя сигналами путей. Нужно было постоянно делать это вручную. Как-то я заскучал и написал на Python код, который загрузил в «Яндекс.Станцию». Теперь, когда я говорю: «Алиса, включи станцию угля», — у меня автоматически включается эта станция. Я сделал это за два дня.
А недавно знакомая попросила написать ей бота для онлайн-магазина. Он должен вести клиента по определённому маршруту и предлагать товары. Это заняло всего 10 часов».
Оценка: автоматизация и скрипты —
Машинное обучение
В ML, Big Data, AI и других модных словах Python — настоящий король. Он легко обходит главных конкурентов — R и Julia (см. нашу статью о языках программирования для ML). На Python собрано больше всего ML-проектов на GitHub. Лидирует он и в авторитетном рейтинге Towards Data Science.


А ещё у Python куча специализированных библиотек.

Алексей Некрасов
Лидер направления Python в МТС, программный директор направления Python в Skillbox
«Я давно занимаюсь инвестициями — в «Тинькофф.Пульсе» можете найти меня под ником znbiz, а с недавних пор я ещё и разрабатываю свою платформу для управления инвестиционными портфелями. Вот как мне помог Python:
Оценка: машинное обучение —
Анализ данных
В Python-библиотеках есть всё необходимое для работы с данными и их визуализации. Например, чтобы быстро создавать структуры и управлять ими, используют библиотеку Pandas. А визуализируют их с помощью Matplotlib, Seaborn или Plotly. Они позволяют рисовать много разных диаграмм и матриц.
Одна из фундаментальных библиотек — NumPy, она подходит для сложных вычислений и поддерживает многомерные массивы. Для работы с многомерными данными также используют набор инструментов PyOD, для индексации документов на естественном языке — Gensim, а со статистическими расчётами отлично справляется SciPy.
Поэтому Python по праву возглавляет рейтинги языков программирования для Data Analysis.

Также в Python предусмотрены интерфейсы для всех популярных реляционных баз данных — Sybase, Oracle, Informix, ODBC, MySQL, PostgreSQL, SQLite и так далее.

Сергей Гилев
Директор по аналитике компании Playkot
«Python — идеальный инструмент для анализа данных в любой сфере. Это было понятно, ещё когда я учился в университете на мехмате и изучал язык С. Я хотел освоить Python, но тогда у меня не было реальных задач, поэтому я придумал себе задачу сам. Как раз набирал обороты Instagram, и я увидел, что мои фото получают разное количество лайков. Мне стало интересно проанализировать, от чего зависит их количество.
В то время я ещё почти ничего не знал про Python и анализ данных. Но благодаря этой задаче разобрался с API Instagram, освоил пакет request, научился работать с БД из Python. В итоге регулярно собирал данные, сколько лайков и комментариев набирали фотографии, — потом это пригодилось в работе».
Оценка: анализ данных —
Драйверы и программирование железа
Python используют, чтобы запрограммировать различные устройства, но это не самый популярный язык для драйверов. Программы на Python часто разворачивают в среде более крупных приложений. Например, для тестирования аппаратных устройств программы на Python могут обращаться к разным компонентам, которые умеют работать на аппаратном уровне. А на GitHub можно найти множество примеров самописных драйверов для джойстиков и контроллеров.

Драйверы на Python пишут для различных ОС — например, вот интересный пост о драйверах PlayStation, написанных на Python под Linux. У некоторых брендов есть даже свои Python-библиотеки с набором модулей — как, например, у компании NI, которая делает оборудование и ПО для автопрома, космоса, оборонки и энергетики.
Правда, у Python есть большая проблема — низкая скорость исполнения. Поэтому драйвера на нём подходят лишь для тех устройств, которые не особо требовательны к ресурсам. Под видеокарты драйвера обычно пишут на более скоростных и низкоуровневых языках — C, C++, Assembler.

Алексей Фирсов
Руководитель Python-практики в компании S7 TechLab
«Python позволяет быстро написать драйвера для любого железа. Когда я работал в компании, которая занималась киберпрограммированием и офлайн-квестами, у нас появилась задача — запрограммировать контроллеры, чтобы двери во время квеста открывались в нужное время. Мы написали их на Python — всё работало хорошо и стабильно.
Ещё один пример программирования контроллеров — программа лояльности. Я написал драйвер для сканера штрихкодов за три часа. В тест система ушла уже на следующий день, а в продакшн — через месяц. В итоге сеть два года проработала на этом драйвере. На Node.js это заняло бы гораздо больше времени».
Оценка: драйверы и программирование железа —
Прототипирование
Python быстрее и проще в работе, чем большинство других языков программирования. Это гибкий язык, который очень легко читать и понимать. Python позволяет совместить в одной программе функциональную, объектно-ориентированную, структурную, аспектно-ориентированную парадигмы программирования — так можно быстро опробовать несколько парадигм и выбрать подходящую, не меняя язык.
Кроме того, с точки зрения Python-программ компоненты, написанные на Python и С, выглядят одинаково. Поэтому нередко систему вначале быстро собирают и тестируют на Python, а потом уже переносят самые требовательные к ресурсам компоненты на компилируемые языки типа С или C++.
Высокая скорость разработки прототипов возможна благодаря большому количеству библиотек и динамической типизации Python. Поэтому его активно используют для экстремального программирования и проверки гипотез.

Алексей Фирсов
Руководитель Python-практики в компании S7 TechLab
«Для любого прототипа подойдёт Python, но только до достижения определённого количества пользователей, которые одновременно будут работать с сервисом. Для меня это планка в 10 тысяч человек. Когда она будет пройдена, стоит подумать про Go. Хотя возможностей Python может хватить и для этого числа пользователей — всё зависит от проекта.
Но эта особенность не должна останавливать проекты на Python, потому что при масштабировании проект всё равно переписывают, на каком бы языке он ни был написан. Ведь за время роста меняются технологии, появляются новые фреймворки — переделок не избежать».
GameDev
Хотя стандартом отрасли считаются языки С и C++, Python также можно встретить в игровой индустрии. Да, на Python не пишется ядро игр, но его применяют для описания логики и сценариев. Например, на Python пишет игры компания CCP Games — та же MMORPG EVE Online почти полностью написана на «удаве». При этом в игре одновременно находится от 15 до 50 тысяч игроков — и она неплохо выдерживает такую нагрузку.

Python используют и в культовом World of Tanks — для некоторых компонентов интерфейса и внутриигровых скриптов. Например, код на Python отвечает за состояние маркеров и прицелов (для каждого типа есть свой Python-класс). А вот за расположение маркеров и прицелов на экране отвечает уже клиентский C++-код.

Чаще всего Python используется в разработке игр как дополнительный, встраиваемый в движок скриптовый язык. Программирование игр и создание мультимедийного контента возможно с помощью библиотек pygame, cgkit, pyglet, РуSoy, PandaBD. Но всё-таки Python — далеко не самый популярный инструмент для геймдева. Делать на нём сложную красивую графику и движки требовательных к ресурсам игр — не лучшее решение.
Минусы Python
Одним из недостатков Python называют его интерпретируемость. Это замедляет работу масштабных проектов. Считается, что, если ваш проект рассчитан на плотную нагрузку, вам больше подойдут Go или C++ — у скомпилированных языков процесс обработки происходит быстрее. По этой же причине опытные разработчики не советуют обрабатывать видео на Python.

Алексей Фирсов
Руководитель Python-практики в компании S7 TechLab
«Я бы не советовал делать на Python сложный рендеринг видео — например, как на YouTube. Python всё равно проиграет в скорости».
Но у интерпретируемости есть и преимущество — писать программы на Python гораздо быстрее, а объём кода обычно в 3−5 раз меньше аналогичных листингов Java и в 5−10 раз меньше эквивалентного кода на C++.
Зачастую Python-код в 1000 раз медленнее аналогичного кода на C/C++. Он не подходит для ПО, которое работает в режиме реального времени и требует минимальных задержек. Тем не менее Python уже неоднократно оптимизировали, и в большинстве сфер он работает достаточно шустро.
Так что всякий раз, когда вы пишете на Python задачу вроде обработки файла или конструирования графического пользовательского интерфейса, программа будет выполняться со скоростью языка С, потому что тут же привязывается к скомпилированному коду на С внутри интерпретатора Python. В итоге выигрыш в скорости разработки на Python чаще всего оказывается выгоднее, чем любые потери в скорости исполнения, особенно учитывая производительность современных компьютеров.
Ещё один минус и плюс одновременно — динамическая типизация. Она также существенно упрощает и ускоряет процесс кодинга, но увеличивает количество возможных ошибок, особенно у неопытного разработчика. Для масштабных проектов всё-таки больше подойдёт статическая типизация.
Python имеет низкий порог вхождения, простой и понятный синтаксис, лаконичный код. Но простоту для входа новичков эксперты называют и минусом — по их словам, на Python легко написать плохой код.

Алексей Фирсов
Руководитель Python-практики в компании S7 TechLab
«Код, написанный новичком, будет корявым — и хотя он будет работать, потом в этом коде будет тяжело разобраться. Потому что Python даёт карт-бланш: ты можешь делать всё. А раз можно написать код плохо, то большинство напишет плохо, потому что будет лень писать хорошо».
Так для чего же подходит Python
Python — действительно универсальный инструмент. Его с удовольствием используют в проектах по машинному обучению и аналитике данных. Применяют для автоматизации, прототипирования и написания драйверов. Правда, в обработке видео, 3D-графике и GameDev у него есть серьёзные ограничения. Тем не менее Python задействуют и в таких проектах — используют для некоторых компонентов интерфейса и внутриигровых скриптов.
Вы тоже можете научиться решать задачи любого уровня сложности с помощью Python, ведь он просто идеален для новичков или как второй язык. Изучить его можно на нашем курсе «Профессия Python-разработчик».
Clhoee/ Divanta/ Ryad/ Cleanpng / XPS/ Usplash / Alexander Ivanets/ Stocksy / Meery_Mary для Skillbox
Использование asyncio для создания асинхронных драйверов устройств на MicroPython v.1.12
Изученая возможности MicroPython для своих целей натолкнулся на одну из реализаций библиотеки asyncio и, после недолгой переписки с Piter Hinch — автором библиотеки, понял, что мне необходимо глубже разобраться с принципами, базовыми понятиями и типичными ошибками использования методов асинхронного программирования. Тем более, что раздел для начинающих — как раз для меня.
Летом 2020 года вышло обновление этой библиотеки, описанное в asyncio v3, при наличии времени и заинтересованности непременно дополню перевод обновленного Руководства.
Это руководство предназначено для пользователей, имеющих разный уровень опыта работы с asyncio, в том числе содержит специальный раздел для начинающих.
Содержание
0. Введение
0.1.___Установка uasyncio на пустое устройство (hardware)
1. Планирование совместного исполнения программ
1.1.___Модули
2. Библиотека uasyncio
2.1.___Структура программы: цикл обработки событий
2.2.___Сопрограммы
2.2.1.______Постановки в очередь сопрограмм для участия в планировании
2.2.2.______Запуск обратного вызова функции (callback)
2.2.3.______Примечания: сопрограммы как связанные методы. Возвращаемые значения.
2.3.___Задержки
3. Синхронизация и ее классы
3.1.___Блокировка Lock
3.1.1.______Блокировки и тайм-ауты
3.2.___Событие Event
3.2.1.______Значение события
3.3.___Барьер Barrier
3.4.___Семафор Semaphore
3.4.1.______Ограниченный Семафор
3.5.___Очередь Queue
3.6.___Другие Классы синхронизации
4. Разработка классов для asyncio
4.1.___Классы с использованием ожидания await
4.1.1.______Использование в менеджерах контекста
4.1.2.______Await в сопрограмме
4.2.___Асинхронные итераторы
4.3.___Асинхронные менеджеры контекста
5. Исключения от тайм-аутов и из-за отмены задач
5.1.___Исключения
5.2.___Исключения вследствие тайм-аутов и из-за отмены задач
5.2.1.______Отмена задач
5.2.2.______Сопрограммы с таймаутами
6. Взаимодействие с оборудованием
6.1.___Проблемы синхронизации
6.2.___Опрос устройств с помощью сопрограмм
6.3.___Использование потокового механизма
6.3.1.______Пример драйвера UART
6.4.___Разработка драйвера для потокового (Stream) устройства
6.5.___Полный пример: aremote.py Драйвер для приемника ИК-пульта дистанционного управления.
6.6.___Драйвер для датчика температуры и влажности HTU21D.
7. Советы и подсказки
7.1.___Программа зависает
7.2.___uasyncio сохраняет состояние
7.3.___Сборка мусора
7.4.___Тестирование
7.5.___Распространенная ошибка. Это может быть трудно найти.
7.6.___Программирование с использованием сокетов (sockets)
7.6.1.______Проблемы с WiFi
7.7.___Аргументы конструктора цикла событий
8. Примечания для начинающих
8.1.___Проблема 1: циклы событий
8.2.___Проблема 2: методы блокировки
8.3.___Подход uasyncio
8.4.___Планирование в uasyncio
8.5.___Почему совместное, а не потоковое планирование (_thread)?
8.6.___Взаимодействие
8.7.___Опрос (polling)
0. Введение
Большая часть этого документа предполагает некоторое знакомство с асинхронным программированием. Для новичков введение можно найти в разделе 7.
Библиотека uasyncio для MicroPython включает подмножество asyncio библиотеки Python и предназначена для использования на микроконтроллерах. Как таковая она занимает небольшой объем оперативной памяти и настроена на быстрое переключение контекста с нулевым распределением оперативной памяти.
Этот документ описывает использование uasyncio с акцентом на создание драйверов для аппаратных устройств.
Цель состоит в том, чтобы спроектировать драйверы таким образом, чтобы приложение продолжало работать, пока драйвер ожидает ответа от устройства. При этом приложение остается чувствительным к другим событиям и взаимодействию с пользователем.
Еще одна важная область применения asyncio — сетевое программирование: в Интернете можно найти достаточно информации по этой теме.
Обратите внимание, что MicroPython основан на Python 3.4 с минимальными дополнениями Python 3.5. За исключением случаев, подробно описанных ниже, функции asyncio версий старше 3.4 не поддерживаются. Данный документ определяет функции, поддерживаемые в этом подмножестве.
Цель этого руководства — представить стиль программирования, совместимый с CPython V3.5 и выше.
0.1 Установка uasyncio на пустое устройство (hardware)
Рекомендуется использовать прошивку MicroPython V1.11 или новее. На многих платформах установка не требуется, так как uasyncioо уже скомпилирована в сборке. Для проверки достаточно набрать в REPL
Следующие инструкции охватывают случаи, когда модули не установлены предварительно. Модули queues и synchro не являются обязательными, но необходимы для выполнения приводимых здесь примеров.
Устройство, подключенное к интернету
На устройстве, подключенном к Интернету и работающем встроенном программном обеспечении V1.11 или более поздней версии можно выполнить установку с использованием встроенной версии upip. Убедившись, что устройство подключено к вашей сети:
Сообщения об ошибках от upip не слишком полезны. Если вы получили непонятную ошибку, лишний раз проверьте подключение к Интернету.
Аппаратное обеспечение без подключения к интернету (micropip)
Если на устройстве отсутствует подключение к Интернету (например, Pyboard V1.x), проще всего запустить на компьютере установку micropip.py в каталог по вашему выбору, а затем скопировать результирующую структуру каталогов на целевое устройство. Утилита micropip.py работает под Python 3.2 или выше и работает под управлением Linux, Windows и OSX. Подробнее информацию можно найти здесь.
Устройство без подключения к Интернету (копирование исходников)
Если не использовать micropip.py, файлы должны быть скопированы из источника. В следующих инструкциях описывается копирование минимального количества файлов на целевое устройство, а также случай, когда uasyncio, для уменьшения занимаемого объема, необходимо сжать в скомпилированную сборку в виде байт-кода. Для последней версии, совместимой с официальными прошивками, файлы должны быть скопированы с официального сайта micropython-lib.
Клонируйте библиотеку на компьютер командой
На целевом устройстве создайте uasyncio каталог (необязательно в каталоге lib) и скопируйте в него следующие файлы:
• uasyncio/uasyncio/__init__.py
• uasyncio.core/uasyncio/core.py
• uasyncio.synchro/uasyncio/synchro.py
• uasyncio.queues/uasyncio/queues.py
Эти модули uasyncio могут быть сжаты в байткод путем размещения каталога uasyncio и его содержимое в порт каталога modules и перекомпилировав содержимое.
1. Совместное планирование
Техника совместного исполнения нескольких задач широко используется во встроенных системах, что предлагает меньше накладных расходов, чем потоковое (_thread) планирование, избегая многих ловушек, связанных с действительно асинхронными потоками выполнения.
Ниже приведен перечень модулей, которые могут исполняться на целевом устройстве.
1. asyn.py Обеспечивает примитивы синхронизации Lock, Event, Barrier, Semaphore, BoundedSemaphore, Condition, gather. Обеспечивает поддержку отмены задач через классы NamedTask и Cancellable.
2. aswitch.py Представляет классы для сопряжения переключателей и кнопок, а также программный объект с возможностью повторной задержки. Кнопки представляют собой обобщение переключателей, обеспечивающих логическое, а не физическое состояние, а также события, вызываемые двойным и длительным нажатием.
Первые две наиболее полезны, так как они дают видимые результаты при доступе к оборудованию Pyboard.
1. check_async_code.py Утилита написана на Python3для обнаружения конкретных ошибок кодирования, которые может быть трудно найти. См. раздел 7.5.
Каталог benchmarks содержит сценарии для проверки и характеристик планировщика uasyncio.
2. Библиотека uasyncio
Концепция asyncio основана на организации планирования совместного исполнения нескольких задач, которые в этом документе называются сопрограммами.
2.1 Структура программы: цикл событий
Рассмотрим следующий пример:
Выполнение программы продолжается до вызова loop.run_forever. На этом этапе выполнение контролируется планировщиком. Строка после loop.run_forever никогда не будет выполнена. Планировщик исполняет код bar потому что он был помещен в очередь планировщика loop.create_task. В этом тривиальном примере есть только одна сопрограмма bar. Если бы были другие, планировщик исполнял бы их в периоды, когда bar была приостановлена.
Большинство встроенных приложений имеют непрерывный цикл обработки событий. Цикл событий также может быть запущен способом, который разрешает завершение, используя метод цикла событий run_until_complete; это в основном используется в тестировании. Примеры можно найти в модуле astests.py.
Экземпляр цикла событий — это одиночный объект, созданный первым вызовом asyncio.get_event_loop() с двумя необязательными целочисленными аргументами, указывающих количество сопрограмм в двух очередях — стартовавшие и ожидающие. Обычно оба аргумента будут иметь одинаковое значение, равное как минимум числу одновременно исполняемых сопрограмм в приложении. Обычно достаточно значение по умолчанию — 16. Если используются значения не по умолчанию, см. Аргументы конструктора цикла событий ( раздел 7.7.).
Если сопрограмме необходимо вызвать метод цикла события (обычно create_task), вызов asyncio.get_event_loop() (без аргументов) эффективно вернет его.
Сопрограмма создается следующим образом:
Сопрограмма может позволить другим сопрограммам запускаться с помощью await оператора. Сопрограмма должна содержать хотя бы одно await утверждение. Это побуждает сопрограмму выполняться до завершения, прежде чем выполнение перейдет к следующей инструкции. Рассмотрим пример:
Первая строка заставляет код приостанавливаться на время задержки, а другие сопрограммы используют это время для своего исполнения. Задержка, равная 0, приводит к тому, что все ожидающие сопрограммы планируются к исполнению в циклическом порядке до исполнения следующей строки. Смотрите пример roundrobin.py.
2.2.1. Очередь для планирования сопрограммы
2.2.2 Запуск функции обратного вызова (callback)
Обратные вызовы должны быть функциями Python, предназначенными для выполнения за короткий промежуток времени. Это связано с тем, что сопрограммы не будут иметь возможность работать в течение всего времени исполнения такой функции.
Следующие методы класса EventLoop используют обратные вызовы:
Сопрограмма может содержать return инструкцию с произвольными возвращаемыми значениями. Чтобы получить это значение:
Сопрограмма может быть ограничена методами и должна содержать хотя бы одно await утверждение.
Есть два варианта для организации задержек в сопрограммах. Для более длительных задержек и тех случаев, когда продолжительность не должна быть точной, можно использовать:
Во время таких задержек планировщик будет исполнять других сопрограммы. Это может вносить неопределенность по времени, так как вызывающая сопрограмма будет запущена только тогда, когда будет выполнена та, которая выполняется в текущий момент. Величина задержки зависит от разработчика приложения, но, вероятно, будет порядка десятков или сотен мс; это обсуждается далее во Взаимодействии с аппаратными устройствами (Раздел 6).
Очень точные задержки могут быть исполнены с помощью функций utime — sleep_ms и sleep_us. Они лучше всего подходят для коротких задержек, так как планировщик не сможет исполнять другие сопрограммы, пока осуществляется задержка.
3.Синхронизация
Часто возникает необходимость обеспечить синхронизацию между сопрограммами. Распространенный пример — избегать так называемых «условий гонки», когда несколько сопрограмм одновременно требуют за доступ к одному ресурсу. Пример приведен в программе astests.py и обсуждается в документации. Другая опасность — «смертельные объятия», когда каждая сопрограмма ждет завершения другой.
В простых приложениях синхронизация может быть достигнута с помощью глобальных флагов или связанных переменных. Более элегантный подход заключается в использовании классов синхронизации. В модуле asyn.py предложены «микро» реализации классов Event, Barrier, Semaphore и Conditios, предназначеных для использования только с asyncio. Они не являются поточно-ориентированными и не должны использоваться с _thread модулем или обработчиком прерываний, если не указано иное. Так же реализован класс Lock, являющийся альтернативой официальной реализации.
Еще одна проблема синхронизации возникает с сопрограммами-производителями и сопрограммами-потребителями. Сопрограмма-производитель генерирует данные, которые использует сопрограмма-потребитель. Для решения подобных задач asyncio предоставляет класс Queue. Сопрограмма-производитель помещает данные в очередь, в то время как сопрограмма-потребитель ожидает его завершения (с другими операциями, запланированными на время). Класс Queue предоставляет гарантии удаления элементов в том порядке, в котором они были получены. В качестве альтернативы можно использовать класс Barrier, если сопрограмма-производитель должна ждать, пока сопрограмма-потребитель не будет готова получить доступ к данным.
Краткий обзор классов приведен ниже. Более подробно в полной документации.
Lock гарантирует уникальный доступ к общему ресурсу. В следующем примере кода создается экземпляр lock класса Lock, который передается всем клиентам, желающим получить доступ к общему ресурсу. Каждая сопрограмма пытается захватить блокировку, приостанавливая выполнение до тех пор, пока не достигнет успеха:
3.1.1.Блокировки (Lock) и таймауты
На момент написания (5 января 2018 г.) официально разработка uasycio класса Lock не закончена. Если для сопрограммы задан тайм-аут(раздел 5.2.2.), в момент ожидания блокировки при срабатывании тайм-аут будет неэффективным. Он не получит TimeoutError пока не получит блокировку. То же самое относится и к отмене задачи.
Модуль asyn.py предлагает класс Lock, который работает в этих ситуациях. Данная реализация класса менее эффективна, чем официальный класс, но поддерживает дополнительные интерфейсы согласно версии CPython, включая использование менеджера контекста.
Event создает возможность одной или нескольким сопрограммам сделать паузу, пока другая не подаст сигнал об их продолжении. Экземпляр Event становится доступными для всех сопрограмм, использующих его:
Сопрограмма ждет события, объявив await event, после чего выполнение приостанавливается, пока другие сопрограммы не объявят event.set(). Полная информация.
Проблема может возникнуть если event.set() выдается в циклической конструкции; код должен подождать, пока все ожидающие объекты не получат доступ к событию, прежде чем устанавливать его снова. В случае, когда одна coro ожидает событие, это может быть достигнуто путем получения coro-события, очищающего событие:
Сопрограмма, инициирующая событие, проверяет, что оно было обслужено:
В случае, когда несколько coros ждут синхронизации одного события, задачу можно решить с помощью события подтверждения. Каждой coro нужно отдельное событие.
Пример этого приведен в event_test функции в asyntest.py. Это громоздко В большинстве случаев — даже с одним ожидающим coro — класс Barrier, представленный ниже, предлагает более простой подход.
Событие также может обеспечить средство связи между обработчиком прерываний и coro. Обработчик обслуживает аппаратное обеспечение и устанавливает событие, которое проверяется coro уже в обычном режиме.
3.2.1 Значения события
Метод event.set() может принимать необязательное значение данных любого типа. Coro, ожидающая события, может получить его с помощью event.value(). Обратите внимание, что event.clear() будет установлено в значение None. Типичное использование этого для coro, устанавливающего событие — выпустить event.set(utime.ticks_ms()). Любая coro, ожидающая события, может определить возникшую задержку, например, чтобы выполнить компенсацию за это.
Существует два варианта применения класса Barrier.
Во-первых, он может приостановить сопрограмму до тех пор, пока одина или несколько других сопрограмм не завершатся.
Во-вторых, он позволяет нескольким сопрограммам встречаться в определенной точке. Например, производитель и потребитель могут синхронизироваться в точке, где у производителя есть данные, и потребитель готов их использовать. В момент исполнения Barrierможет выполнить дополнительный обратный вызов до того, как барьер будет снят, и все ожидающие события могут продолжаться.
Обратный вызов может быть функцией или сопрограммой. В большинстве приложений, скорее всего, будет использоваться функция: она может быть гарантированно выполнена до завершения, прежде чем барьер будет снят.
Примером является функция barrier_test в asyntest.py. Во фрагменте кода этой программы:
несколько экземпляров сопрограммы report печатают свой результат и делают паузу, пока другие экземпляры также не будут завершены и ожидают продолжения barrier. В этот момент выполняется обратный вызов. По его завершении возобновляется изначальная сопрограмма.
Семафор ограничивает число сопрограмм, которые могут получить доступ к ресурсу. Его можно использовать для ограничения количества экземпляров определенной сопрограммы, которые могут работать одновременно. Это выполняется с использованием счетчика доступа, который инициализируется конструктором и уменьшается каждый раз, когда сопрограмма получает семафор.
Самый простой способ использовать его в менеджере контекста:
Примером является функция semaphore_test в asyntest.py.
Работает аналогично классу Semaphore за исключением того, что, если release метод заставляет счетчик доступа превысить свое начальное значение, a ValueError устанавливается.
Класс Queue поддерживается официальной uasycio и пример программы aqtest.py демонстрирует его использование. Очередь создается следующим образом:
Типичная сопрограмма-производитель может работать следующим образом:
и сопрограмма-потребитель может работать следующим образом:
Класс Queue предоставляет значительные дополнительные функциональные возможности в том случае, когда размер очередей может быть ограничен и может быть опрошен статус. Поведение при пустой очереди (если размер ограничен) и поведение при полной очереди может контролироваться. Документация об этом есть в коде.
3.6 Другие Классы синхронизации
Библиотека asyn.py предоставляет «микро» реализации еще некоторых возможностей CPython.
Класс Condition позволяет сопрограмме уведомлять другие сопрограммы, ождидающие на заблокированном ресурсе. После получения уведомления они получат доступ к ресурсу и снимут блокировку по очереди. Уведомляющая сопрограмма может ограничивать количество сопрограмм, подлежащих уведомлению.
Класс Gather позволяет запустить список сопрограмм. После завершения последней будет возвращен список результатов. Эта «микро» реализация использует другой синтаксис. Тайм-ауты могут быть применены к любой из сопрограмм.
4. Разработка классов для asyncio
В контексте разработки драйверов устройств цель состоит в том, чтобы обеспечить их неблокирующую работу. Сопрограмма-драйвер должна гарантировать, что другие сопрограммы будут исполняться, пока драйвер ожидает исполнения устройством аппаратных операций. Например, задача, ожидающая данных, поступающих в UART, или пользователь, нажимающий кнопку, должен позволять планировать другие события, пока событие не произойдет.
4.1 Классы с использованием ожидания await
Сопрограмма может приостановить выполнение, ожидая объект awaitable. Под CPython пользовательский класс awaitable создается путем реализации специального метода __await__, который возвращает генератор. Класс awaitable используется следующим образом:
В настоящее время MicroPython не поддерживает __await__ (проблема # 2678) и для решения должна использоваться __iter__. Строка __iter__ = __await__ обеспечивает переносимость между CPython и MicroPython. Примеры кода смотрите в классах Event, Barrier, Cancellable, Condition в asyn.py.
4.1.1 Использование в контекстных менеджерах
Ожидаемые объекты могут использоваться в синхронных или асинхронных контекстных менеджерах, предоставляя необходимые специальные методы. Синтаксис:
Для достижения этого __await__ генератор должен вернуть self. Это передается любой переменной в as предложении, а также позволяет работать специальным методам. Посмотрите asyn.Condition и asyntest.condition_test где класс Condition использует await и при этом может использоваться в синхронном контекстном менеджере.
4.1.2 Await в сопрограмме
Язык Python требует, чтобы __await__ было функцией генератора. В MicroPython генераторы и сопрограммы идентичны, поэтому решение заключается в использовании yield from coro(args).
Цель этого руководства — предложить код, переносимый на CPython 3.5 или выше. В CPython генераторы и сопрограммы различны по смыслу. В CPython сопрограмма есть __await__ специальный метод, который извлекает генератор. Это переносимо:
Обратите внимание, что __await__, yield from asyncio.sleep(1) разрешены CPython. Я еще не понял, как это достигается.
4.2 Асинхронные итераторы
Асинхронные итераторы предоставляют средства для возврата конечной или бесконечной последовательности значений и могут использоваться в качестве средства извлечения последовательных элементов данных, когда они поступают с устройства только для чтения. Асинхронный итератор вызывает асинхронный код в своем методе next. Класс должен соответствовать следующим требованиям:
4.3 Асинхронные контекстные менеджеры
Классы могут быть разработаны для поддержки асинхронных контекстных менеджеров, имеющих процедуры входа и выхода, которые являются сопрограмами. Примером является Класс Lock, описанный выше. Он имеет сопрограмму __aenter__, которая логически требуется для асинхронной работы. Для поддержки асинхронного протокола контекстного менеджера его __aexit__ метод также должен быть сопрограмой, что достигается путем включения await asyncio.sleep(0). Такие классы доступны изнутри сопрограммы со следующим синтаксисом:
Как и в случае с обычными менеджерами контекста, гарантированно будет вызван метод выхода, когда менеджер контекста завершит работу, как обычно, так и через исключение. Для достижения этой цели используются специальные методы __aenter__и __aexit__ которые должны быть определены, как сопрограммы, ожидающие другую сопрограмму или awaitable объект. Этот пример взят из класса Lock:
Если async with содержит предложение as variable, переменная получает значение, возвращаемое __aenter__.
Для обеспечения корректного поведения прошивка должна быть V1.9.10 или новее.
5.Исключения от тайм-аутов и из-за отмены задач
Эти темы связаны между собой: uasyncio включает отмену задач и применение тайм-аута к задаче, выдавая исключение для задачи особым образом.
Если в сопрограмме возникает исключение (exeption), оно должно быть обработано либо в этой сопрограмме, либо в сопрограмме, ожидающей ее завершения. Это гарантирует, что исключение не распространяется на планировщик. Если исключение произойдет, планировщик прекратит работу, передав исключение в код, который запустил планировщик. Следовательно, чтобы избежать остановки планировщика, сопрограмма, запущенная с loop.create_task() должна перехватывать любые исключения внутри.
Использование throw или close чтобы запустить исключение в сопрограмме, неразумно. Это разрушает uasyncio, заставляя сопрограмму запускаться и, возможно, завершаться, когда он все еще находится в очереди на исполнение.
Приведенный пример иллюстрирует эту ситуацию. Если разрешено работать до конца, он работает как ожидалось.
Однако выдача прерывания клавиатуры приводит к тому, что исключение переходит в цикл обработки событий. Это связано с тем, что выполнение uasyncio.sleep передается в цикл обработки событий. Следовательно, приложения, требующие кода очистки в ответ на прерывание клавиатуры, должны перехватывать исключение на уровне цикла событий.
5.2 Отмена и таймауты
Как указывалось выше, эти функции работают, вызывая исключение для задачи особым образом, используя специальный метод MicroPython сопрограмму pend_throw. Как это работает, зависит от версии. В официальной uasyncio v.2.0 исключение не обрабатывается до следующего запланированного задания. Это налагает задержку, если задача ожидает sleep ввода-вывода. Тайм-ауты могут выходить за пределы своего номинального периода. Задача отмены других задач не может определить, когда отмена завершена.
В настоящее время существует обходной путь и два решения.
5.2.1 Отмена задания
uasyncio обеспечивает функцию cancel(coro). Это работает, выбрасывая исключение для использования сопрограммы pend_throw. Так же это работает с вложенными сопрограммами. Использование заключается в следующем:
Если этот пример запустить под uasyncio v2.0, то когда bar выдаст cancel он не вступит в силу до следующего запланированного foo и при отмене foo может возникнуть задержка до 10 секунд. Другой источник задержки возникнет, если foo ожидает ввода-вывода. Где бы задержка не возникла, bar не сможет определить, была ли foo отменена. Это имеет значение в некоторых случаях использования.
При использовании библиотек Paul Sokolovsky или fast_io достаточно применить sleep(0):
Это также будет работать в uasyncio v2.0, если foo (и любой ожидающей сопрограммы foo) никогда не выдавал sleep и не ождая ввода / вывода.
Поведение, которое может удивить неосторожного, возникает, когда ожидается отмена сопрограммы, запущенной create_task и находящеяся в режиме ожидания. Рассмотрим этот фрагмент:
Когда foo отменяется, она удаляется из очереди планировщика; потому что в ней отсутствует return инструкция, вызывающая процедура foo_runner никогда не возобновляется. Рекомендуется всегда перехватывать исключение в самой внешней области действия функции, подлежащей отмене:
В этом случае my_coro не нужно перехватывать исключение, так как оно будет распространено в вызывающий канал и захвачено там.
Примечание. Запрещается использовать методы close или throw методы сопрограмм, когда сопрограмма используется вне планировщика. Это подрывает планировщик, заставляя сопрограмму выполнять код, даже если он не запланирован. Это может иметь нежелательные последствия.
5.2.2 Сопрограммы с таймаутами
Таймауты реализуются с помощью uasyncio методов .wait_for() и .wait_for_ms(). Они принимают в качестве аргументов сопрограмму и время ожидания в секундах или мс соответственно. Если тайм-аут истекает, TimeoutError будет вброшен в сопрограмму с помощью pend_throw. Это исключение должно быть перехвачено либо пользователем, либо вызывающим абонентом. Это необходимо по причине, описанной выше: если время ожидания истекает, оно отменяется. Если только ошибка не будет перехвачена и не вернётся, единственный путь, по которому вызывающий может продолжить, — это перехват самого исключения.
Там, где исключение перехвачено сопрограммой, у меня возникли неясные сбои, если исключение не перехвачено во внешней области видимости, как показано ниже:
В качестве альтернативы можно перехватить вызывающей функцией:
Примечение для Uasyncio v2.0.
Это не применимо к библиотекам Paul Sokolovsky или fast_io.
Если сопрограмма запускает await asyncio.sleep(t), с большой задержкой t, сопрограмма не будет перезагружена до истечения t. Если тайм-аут истек до того, как завершится sleep, произойдет TimeoutError при перезагрузке сопрограммы — т.е. когда истечет t. В режиме реального времени и с точки зрения вызывающего абонента, его ответ TimeoutError будет отложен.
Если это важно для приложения, создайте длинную задержку, ожидая короткую в цикле. Сопрограмма asyn.sleep поддерживает это.
6. Взаимодействие с оборудованием
В основе взаимодействия между uasyncio и внешними асинхронными событиями лежат опросы (polling). Аппаратное обеспечение, требующее быстрого ответа, может использовать прерывание. Но взаимодействие между подпрограммой обработки прерываний (ISR) и пользовательской сопрограммой будет основано на опросах. Например, ISR может вызвать Event или установить глобальный флаг, в то время как сопрограмма, ожидающая результата, опрашивает объект каждый раз, когда запрос запланирован.
Опрос может осуществляться двумя способами, явным или неявным. Последнее выполняется с использованием stream I/O механизма, который представляет собой систему, предназначенную для потоковых устройств, таких как UART и сокеты. В самом простом явном опросе может состоять такой код:
Вместо глобального флага можно использовать переменную экземпляра класса Event или экземпляр класса, использующего await. Явный опрос обсуждается ниже.
Неявный опрос состоит из разработки драйвера, который будет работать как потоковое устройство ввода-вывода, такое как UART или сокет stream I/O, который опрашивает устройства, используя систему select.poll Python: поскольку опрос выполняется в C, он быстрее и эффективнее, чем явный опрос. Использование stream I/O обсуждается в разделе 6.3.
Благодаря своей эффективности неявный опрос дает преимущество наиболее быстрым драйверам устройств ввода / вывода: потоковые драйверы могут быть созданы для многих устройств, которые обычно не рассматриваются как потоковые устройства. Более подробно это обсуждается в разделе 6.4.
6.1 Проблемы синхронизации
И явный, и неявный опрос в настоящее время основаны на циклическом планировании. Предположим, что ввод-вывод работает одновременно с N пользовательскими сопрограммами, каждая из которых исполняется с нулевой задержкой. Когда ввод-вывод будет обслужен, он будет затем опрошен, как только все пользовательские операции будут запланированы. Предполагаемая задержка должна учитываться при проектировании. Каналы ввода / вывода могут потребовать буферизации, при этом ISR обслуживает оборудование в режиме реального времени от буферов и сопрограмм, заполняя или освобождая буферы в более медленное время.
Также необходимо учитывать возможность выхода за пределы: это тот случай, когда что-то, опрашиваемое сопрограммой, происходит более одного раза до того, как на самом деле запланирована сопрограммой.
Другой проблемой синхронизации является точность задержек. Если сопрограмма издает
планировщик гарантирует, что выполнение будет приостановлено как минимум на t мс. Фактическая задержка может быть больше чем t, что зависит от текущей загрузки системы. Если в это время другие сопрограммы находятся в ожидании завершения ненулевых задержек, следующая строка будет немедленно запланирована к исполнению. Но если другие сопрограммы так же ожидают выполнения (либо потому, что они издали нулевую задержку, либо потому, что их время также истекло), они могут быть запланированы к исполнению раньше. Это вносит неопределенность синхронизации в sleep() и sleep_ms() функции. Значение наихудшего случая для этого переполнения может быть рассчитано путем суммирования значений времени выполнения всех таких сопрограмм для определения наихудшего времени передачи в планировщик.
Fast_io версии uasyncio в этом контексте обеспечивает способ гарантировать, что потоковый ввод/вывод будет опрашиваться на каждой итерации планировщика. Есть надежда, что официальное uasyncio примет соответствующие поправки в свое время.
6.2 Опрос устройств с помощью сопрограмм
Это простой подход, который наиболее подходит для устройств, которые могут опрашиваться с относительно низкой скоростью. Это связано прежде всего с тем, что опрос с коротким (или нулевым) интервалом опроса может привести к тому, что сопрограмма потребляет больше процессорного времени, чем желательно для попадания в интервал.
Пример apoll.py демонстрирует этот подход, опрашивая акселерометр Pyboard с интервалом 100 мс. Он выполняет простую фильтрацию, чтобы игнорировать шум, и печатает сообщение каждые две секунды, если перемещения не происходит.
Пример aswitch.py представляет драйверы для переключателей и кнопочных устройств.
Пример драйвера для устройства, способного считывать и записывать, показан ниже. Для удобства тестирования Pyboard UART 4 эмулирует условное устройство. Драйвер реализует Класс RecordOrientedUart, в котором данные поставляются в записях переменной длины, состоящих из байтовых экземпляров. Объект добавляет разделитель перед отправкой и буферизует входящие данные, пока не будет получен добавленный разделитель. Это лишь демонстрационная версия и неэффективный способ использования UART по сравнению с потоковым вводом/выводом.
В целях демонстрации асинхронной передачи мы предполагаем, что эмулируемое устройство имеет средство проверки того, что передача завершена и что приложение требует, чтобы мы подождали этого. Ни одно из предположений не является верным в этом примере, но код подделывает его путем await asyncio.sleep(0.1).
Для запуска не забудьте соединить выводы Pyboard X1 и X2 (UART Txd и Rxd)
6.3 Использование потокового механизма (Stream)
В примере демонстрируется одновременный ввод-вывод на одном UART микропроцессора Pyboard.
Для запуска следует соединить выводы Pyboard X1 и X2 (UART Txd и Rxd)
Поддерживающий код можно найти в __init__.py в uasyncio библиотеке. Механизм работает, так как драйвер устройства (написанный на C) реализует следующие методы: ioctl, read, readline и write. Раздел 6.4.Написание драйвера потокового устройства раскрывает подробности о том, как такие драйверы могут быть написаны на Python.
UART может получать данные в любое время. Механизм потокового ввода-вывода проверяет наличие ожидающих входящих символов всякий раз, когда планировщик получает контроль. Когда сопрограмма работает, программа обработки прерываний буферизует входящие символы; они будут удалены, когда сопрограмма уступит время планировщику. Следовательно, приложения UART должны быть спроектированы таким образом, чтобы сопрограммы минимизировали время между передачей в планировщик, чтобы избежать переполнения буфера и потери данных. Это можно улучшить, используя больший буфер чтения UART, либо меньшую скорость передачи данных. В качестве альтернативы аппаратное управление потоком обеспечит решение, если источник данных его поддерживает.
6.3.1 Пример драйвера UART
Программа auart_hd.py иллюстрирует способ связи с полудуплексным устройством, таким как устройство, отвечающее на набор команд модема «AT». Полудуплекс означает, что устройство никогда не отправляет незапрошенные данные: его передачи всегда выполняются в ответ на полученную команду от мастера.
Устройство эмулируется путем запуска тест на Pyboard с двумя проводными связями.
(Очень упрощенное) эмулируемое устройство реагирует на любую команду, посылая четыре строки данных с паузой между каждой, чтобы имитировать медленную обработку.
Мастер отправляет команду, но заранее не знает, сколько строк данных будет возвращено. Он запускает таймер повторного запуска, который перезапускается каждый раз при получении строки. Когда таймер истекает, предполагается, что устройство завершило передачу, и возвращается список принятых строк.
Также продемонстрирован случай отказа устройства, что достигается путем пропуска передачи перед ожиданием ответа. После истечения времени ожидания возвращается пустой список. Смотрите комментарии к коду для более подробной информации.
6.4 Разработка драйвера потокового (Stream) устройства
Механизм потокового ввода/вывода (stream I/O) предназначен для управления работой потоковых устройств ввода/вывода, таких как UART и сокеты(socket). Механизм может использоваться драйверами любого регулярно опрашиваемого устройства путем делегирования планировщику, который использует select, опроса готовности любых устройств в очереди. Это более эффективно, чем выполнение нескольких операций сопрограмм, каждый из которых опрашивает устройство, отчасти потому, что select написано на C, а также потому, что сопрограмма, выполняющая опрос, откладывается до тех пор, пока опрашиваемый объект не вернет состояние готовности.
readline() Вернуть столько символов, сколько доступно, вплоть до любого символа новой строки. Требуется, если использовать StreamReader.readline()
read(n) Вернуть столько символов, сколько доступно, но не более n. Требуется, если использовать StreamReader.read() или StreamReader.readexactly()
Создаваемый драйвер должен обеспечить следующий синхронный метод при этом с немедленным возвратом:
write с аргументами buf, off, sz.
buf — это буфер для записи.
off — смещение в буфер первого символа для записи.
sz — запрашиваемое количество символов для записи.
Возвращаемое значение — количество фактически написанных символов (может быть 1, если устройство работает медленно).
Метод ioctl гарантирует, что будет вызван только тогда, когда устройство будет готово к приему данных.
Все устройства должны обеспечивать метод ioctl, который опрашивает оборудование для определения его состояния готовности. Типичный пример для драйвера чтения/записи:
Ниже приведено описание Класса MillisecTimer ожидания задержки:
который может быть использован следующим образом:
По сравнению с официальной uasyncio подобная реализация не дает никаких преимуществ по сравнению с await asyncio.sleep_ms(). Применение fast_io обеспечивает значительно более точные задержки при обычной схеме использования, когда сопрограммы ожидают нулевую задержку.
Можно использовать планирование ввода / вывода, чтобы связать событие с обратным вызовом. Это более эффективно, чем цикл опроса, потому что выполнение опроса не запланировано, пока ioctl не вернется состояние готовности. Далее выполняется обратный вызов, когда обратный вызов меняет состояние.
И вновь — на официальном uasyncio задержка может высокой. В зависимости от дизайна приложения версия fast_io может оказаться более эффективной.
Демонстрационная программа iorw.py иллюстрирует полный пример. Обратите внимание, что на момент написания статьи в официальном uasyncio есть ошибка, из-за которой это не работает. Есть два решения. Обходной путь — написать два отдельных драйвера, один только для чтения, а другой только для записи. Второй — использовать fast_io, который решает эту проблему.
В официальном uasyncio ввод/вывод планируется довольно редко.
6.5 Полный пример: aremote.py
Драйвер предназначен для приема/декодирования сигналов с инфракрасного пульта дистанционного управления. Сам драйвер aremote.py. Следующие примечания являются существенными касательно использования asyncio.
Прерывание на контакте записывает время изменения состояния (в мкс) и устанавливает событие, пропуская время, когда произошло первое изменение состояния. Сопрограмма ожидает события, выдает длительность пакета данных, затем декодирует сохраненные данные перед вызовом указанного пользователем обратного вызова.
Передача времени экземпляру Event позволяет сопрограмме компенсировать любую asyncio задержку при настройке периода задержки.
6.6 HTU21D датчик окружающей среды
Драйвер чипа HTU21D обеспечивает точные измерения температуры и влажности.
Чипу требуется порядка 120 мс, чтобы получить оба элемента данных. Драйвер работает асинхронно, инициируя получение и использование await asyncio.sleep(t) до чтения данных, обновляет переменные temperature и humidity, к которым можно получить доступ в любой момент, что позволяет другим сопрограммам запускаться во время работы драйвера чипа.
7. Советы и подсказки
7.1 Программа зависает
Зависание обычно происходит из-за того, что задача заблокирована без уступки: это приведет к зависанию всей системы. При разработке полезно иметь сопрограмму, которая периодически включает встроенный светодиод. Это обеспечивает подтверждение того, что планировщик еще работает.
7.2 uasyncio сохраняет состояние
При запуске программ, использующих uasyncio в REPL, выполните программный сброс (ctrl-D) между запусками. В связи с тем, что uasyncio сохраняет состояние между запусками, при очередном запуске может происходить непредсказуемое поведению.
Можно исполнить сопрограмму, предварительно указав import gc:
Цель этого обсуждается здесь, в разделе о куче (heap).
Желательно убедиться в том, что драйвер устройства удерживает контроль, когда это необходимо, что можно сделать, запустив один или несколько экземпляров фиктивных сопрограмм, запускающих цикл печати сообщения и проверяя, что оно выполняется в периоды, когда драйвер находится в режиме ожидания:
В качестве примера типа опасности, которая может возникнуть, в приведенном выше примере RecordOrientedUart __await__ метод изначально был записан как:
В результате исполнение растягивается до тех пор, пока не будет получена вся запись, а так же с тем, что uart.any() всегда возвращает ненулевое количество полученных символов. К моменту вызова все символы могут быть уже получены. Такую ситуацию можно разрешить использованием внешнего цикла:
Возможно, стоит отметить, что эта ошибка не была бы очевидной, если бы данные отправлялись в UART с более низкой скоростью, а не с помощью теста с обратной связью. Добро пожаловать в радости программирования в реальном времени.
7.5 Распространенная ошибка
Если функция или метод определены async def и впоследствии вызваны так, как если бы они были обычными (синхронными) вызываемыми, MicroPython не выдает сообщение об ошибке. Это по замыслу. Обычно это приводит к тому, что программа молча не работает правильно:
У меня есть предложение, которое предлагает ситуацию исправить в 1 варианте с помощью fast_io.
Обратите внимание, что он несколько грубоват и предназначен для использования в синтаксически правильном файле, который по умолчанию не запускается. Используйте инструмент, например, pylint для общей проверки синтаксиса (в pylint в настоящее время эта ошибка отсутствует).
Сценарий выдает ложные срабатывания. По замыслу сопрограммы являются объектами первого уровня, их можно передавать функциям и хранить их в структурах данных. В зависимости от логики программы можно сохранить функцию или результат ее выполнения. Сценарий не может определить намерение. Он направлен на игнорирование случаев, которые кажутся правильными, при выявлении других случаев для рассмотрения. Предположим foo, где сопрограмма объявлена как async def:
Я нахожу это полезным как есть, но улучшения всегда приветствуются.
7.6 Программирование с использованием сокетов (sockets)
Существует два основных подхода к программированию сокетов uasyncio. По умолчанию, сокеты блокируются до завершения указанной операции чтения или записи. Uasyncio поддерживает блокировку сокетов, используя select.poll для предотвращения их блокирования планировщиком. В большинстве случаев проще всего использовать именно этот механизм. Пример клиентского и серверного кода можно найти в каталоге client_server. Userver использует приложение select.poll явно опрашивая сокет сервера.
Клиентские сокеты используют его неявно в том смысле, что потоковый механизм uasyncio непосредственно использует его.
Обратите внимание, что socket.getaddrinfo в настоящее время блокируемые. Время в примере кода будет минимальным, но если требуется поиск DNS, период блокировки может быть значительным.
Второй подход к программированию сокетов заключается в использовании неблокирующих сокетов. Это добавляет сложности, но необходимо в некоторых приложениях, особенно если подключение осуществляется через WiFi (см. Ниже).
На момент написания статьи (март 2019 г.) поддержка TLS для неблокирующих сокетов находилась в стадии разработки. Его точный статус неизвестен (мне).
Использование неблокирующих сокетов требует некоторого внимания к деталям. Если неблокирующее чтение выполняется из-за задержки сервера, нет гарантии, что все (или любые) запрошенные данные будут возвращены. Аналогичным образом записи могут не перейти к завершению.
Следовательно, асинхронные методы чтения и записи должны итеративно выполнять неблокирующую операцию, пока требуемые данные не будут прочитаны или записаны. На практике может потребоваться тайм-аут, чтобы справиться с перебоями в работе сервера.
Еще одним осложнением является то, что порт ESP32 имел проблемы, которые требовали довольно неприятных взломов для безошибочной работы. Я не проверял, так ли это до сих пор.
Модуль sock_nonblock.py иллюстрирует требуемые методы. Это не рабочая демонстрация и решения, вероятно, будут зависеть от приложения.
7.6.1 Проблемы с WiFi
Потоковый механизм uasyncio не лучший вариант при обнаружении WiFi отключений. Я счел необходимым использовать неблокирующие сокеты для обеспечения отказоустойчивой работы и переподключения клиента при наличии сбоев.
Этот документ описывает проблемы, с которыми я столкнулся в приложениях WiFi, которые держат сокеты открытыми в течение длительных периодов, и обрисовывает в общих чертах решение.
Pltcm предлагает устойчивый асинхронный MQTT-клиент, который обеспечивает целостность сообщений при сбоях WiFi. Описан простой асинхронный полнодуплексный последовательный канал между беспроводным клиентом и проводным сервером с гарантированной доставкой сообщений.
7.7 Аргументы конструктора цикла событий
Небольшая ошибка может возникнуть, если вам нужно создать цикл обработки событий со значениями, отличающимися от заданных по умолчанию. Такой цикл необходимо декларировать перед запуском любого другого кода с использованием asyncio потому, что эти значения могут потребоваться в этом коде. В противном случае код будет инициализирован значениями по умолчанию:
Учитывая, что импорт модуля может выполнять код, самый безопасный способ — создать экземпляр цикла событий сразу после импорта uasyncio.
Я предпочтитаю при написании модулей для использования другими программами избегать запуска кода uasyncio при импорте. Напишите функции и методы для ожидания цикла событий как аргумента. Затем убедитесь, что только приложения верхнего уровня вызывают get_event_loop:
Этот вопрос обсуждается здесь.
8 заметок для начинающих
Эти заметки предназначены для новичков в асинхронном коде и начинаются с описания проблем, которые стараются решить планировщики, а так же дают обзор подхода uasyncio к решениям.
Раздел 8.5 обсуждает относительные достоинства модулей uasyncio и _thread, а так же почему вы можете предпочесть использование сопрограмм uasyncio упреждающему планированию ( _thread).
8.1 Проблема 1: циклы событий
Типичное приложение прошивки работает непрерывно и при этом должно реагировать на внешние события, которые могут включать в себя изменение напряжения на АЦП, появление аппаратного прерывания, или символа поступившего в UART, или данных доступных в сокете. Эти события происходят асинхронно, и код должен иметь возможность отвечать независимо от того, в каком порядке они происходят. Кроме того, может потребоваться выполнение задач, зависящих от времени, например, мигание светодиодов.
Очевидный способ сделать это с помощью цикла событий uasycio. Этот пример не является практическим кодом, но служит для иллюстрации общей формы цикла событий.
Такой цикл работает для простых примеров, но по мере увеличения количества событий код быстро становятся громоздкими. Они также нарушают принципы объектно-ориентированного программирования, объединяя большую часть программной логики в одном месте, а не связывая код с контролируемым объектом. Мы хотим разработать класс для светодиода, способного мигать, который можно вставить в модуль и импортировать. Подход ООП к миганию светодиода может выглядеть следующим образом:
Планировщик в uasyncio позволяет создавать такие классы.
8.2 Проблема 2: методы блокировки
Предположим, вам нужно прочитать некоторое количество байтов из сокета. Если вы вызываете socket.read(n) с блокирующим сокетом по умолчанию, он будет «блокироваться» (то есть не сможет завершиться), пока не будут получены n байтов. В течение этого периода приложение не будет реагировать на другие события.
С помощью неблокирующего сокета uasyncio вы можете написать метод асинхронного чтения. Задача, требующая данных, будет (обязательно) блокироваться до тех пор, пока они не будут получены, но в течение этого периода будут выполняться другие задачи, что позволит приложению оставаться отзывчивым.
8.3. Подходы uasyncio
В следующем классе предусмотрен светодиод, который можно включать и выключать, а также можно мигать с произвольной скоростью. Экземпляр LED_async использует метод run, который можно использовать для непрерывной работы. Поведением светодиодов можно управлять с помощью методов on(), off() и flash(secs).
Следует отметить, что on(), off() и flash() представляют из себя обычные синхронные методы. Они изменяют поведение светодиода, но возвращаются немедленно. Мигание происходит «в фоновом режиме». Это подробно объясняется в следующем разделе.
Класс соответствует принципу ООП, заключающемуся в сохранении логики, связанной с устройством, в классе. При этом, применение uasyncio гарантирует, что во время мигания светодиода приложение может реагировать на другие события. Программа, приведенная ниже, мигает четырьмя светодиодами Pyboard с различной частотой, а также реагирует на кнопку USR, которая ее завершает.
В отличие от первого примера цикла событий логика, связанная с переключателем, находится в функции, отдельной от функциональности светодиода. Обратите внимание на код, используемый для запуска планировщика:
8.4 Планирование в uasyncio
Python 3.5 и MicroPython поддерживают понятие асинхронной функции, также известной как сопрограмма или задача. Сопрограмма должна включать хотя бы одно утверждение await.
Эта функция печатает сообщение десять раз с интервалом в одну секунду. Пока функция приостановлена в ожидании задержки, планировщик asyncio будет исполнять другие задачи, создавая иллюзию их одновременного выполнения.
Когда сопрограмма выдает await asyncio.sleep_ms() или await asyncio.sleep() текущая задача приостанавливается и помещается в очередь, которая упорядочена по времени и выполнение переходит к задаче, находящейся вверху очереди. Очередь спроектирована таким образом, что, даже если указанный спящий режим равен нулю, другие соответствующие задачи будут выполняться до возобновления текущей. Это «честное круговое» планирование. Обычной практикой является выполнение циклов await asyncio.sleep(0), чтобы задача не задерживала выполнение. Далее показан цикл «занято-ожидание», ожидающий другой задачи для установки глобальной переменной flag. Увы, он монополизирует процессор, предотвращая запуск других сопрограмм:
Проблема здесь в том, что до тех пор, пока цикл flagis False управление не будет передано планировщику, поэтому никакая другая задача не будет запущена. Правильный подход:
По той же причине плохая практика задавать задержки, например, utime.sleep(1) потому что это блокирует другие задачи на 1 с; правильнее использовать await asyncio.sleep(1).
Обратите внимание, что задержки, формируемые методами uasyncio sleep и sleep_ms в реальности могут превышать указанное время. Это связано с тем, что во время задержки будут выполняться другие задачи. По истечении периода задержки выполнение не возобновится до тех пор, пока запущенная задача не выдаст await или не завершит работу. Хорошо себя ведущая сопрограмма всегда будет декларировать await через регулярные промежутки времени. Там, где требуется точная задержка, особенно если одна меньше нескольких мс, возможно необходимо использовать utime.sleep_us(us).
8.5 Почему совместное, а не потоковое планирование (_thread)?
Первоначальная реакция начинающих на идею совместного планирования сопрограмм часто вызывает разочарование. Наверняка потоковое планирование лучше? Почему я должен явно уступать контроль, если виртуальная машина Python может сделать это для меня?
Когда речь идет о встроенных системах, модель совместной работы имеет два преимущества.
Во-первых, это легкий вес. Возможно иметь большое количество сопрограмм, потому что в отличие от запланированных потоков, приостановленные сопрограммы занимают меньше места.
Во-вторых, это позволяет избежать некоторых тонких проблем, связанных с потоковым планированием.
На практике совместная многозадачность используется широко, особенно в приложениях с пользовательским интерфейсом.
В защиту модели с потоковым планированием покажу одно преимущество: если кто-то пишет
это не заблокирует другие задачи. В модели совместной работы предполагается, что цикл должен явно давать контроль каждой задаче определенное количество итераций, например, помещая код в сопрограмму и периодически выпуская await asyncio.sleep(0).
Увы, это преимущество бледнеет по сравнению с недостатками. Некоторые из них описаны в документации по написанию обработчиков прерываний. В модели c потоковым планированием каждый поток может прерывать любой другой поток, изменяя данные, которые могут использоваться в других потоках. Как правило, гораздо проще найти и исправить блокировку, возникающую из-за ошибки, которая не дает результата, чем обнаружение иногда очень тонких и редко встречающихся ошибок, которые могут возникнуть в коде, написанном в рамках модели c потоковым планированием.
Проще говоря, если вы напишите сопрограмму MicroPython, вы можете быть уверены, что переменные не будут внезапно изменены другой сопрограммой: ваша сопрограмма имеет полный контроль, пока не выдаст await asyncio.sleep(0).
Имейте в виду, что обработчики прерываний являются преимущественными. Это относится как к аппаратным, так и к программным прерываниям, которые могут произойти в любой точке вашего кода.
Красноречивое обсуждение проблем потокового планирования можно найти здесь.
В нетривиальных приложениях сопрограммы должны взаимодействовать. При этом могут быть использованы обычные методы Python. К ним относится использование глобальных переменных или объявление сопрограмм в качестве методов объекта: они могут совместно использовать переменные экземпляра. В качестве альтернативы изменяемый объект может быть передан в качестве аргумента сопрограммы.
Модель c потоковым планированием требуют от специалистов, чтобы классы обеспечивали безопасную связь; в модели совместной работы это редко требуется.
Некоторые аппаратные устройства, такие как акселерометр Pyboard, не поддерживают прерывания, и поэтому должны опрашиваться (то есть периодически проверяться). Опрос также может использоваться в сочетании с обработчиками прерываний: обработчик прерываний обслуживает оборудование и устанавливает флаг. Сопрограмма опрашивает флаг — если он установлен, происходит обработка данных и флаг сбрасывается. Лучшим подходом является использование класса Event.