Основы асинхронности в Python #2: Асинхронность с простыми функциями. Событийный цикл.
HTML-код
- Опубликовано: 2 янв 2025
- Мои курсы:
Boosty:
boosty.to/omol...
Patreon:
/ karty-vsekh-41011404
Основы асинхронности в Python для начинающих, она же "кооперативная многозадачность".
Речь в этой серии пойдет о коде, который асинхронно выполняется в одном потоке и в одном процессе.
В этом видео:
Рефакторим код предыдущего примера так, чтобы у нас появилась возможность в принципе выполнять его асинхронно.
Создаем простейший событийный цикл (Event Loop) и знакомимся с функцией select().
Ключевые моменты этого видео - рефакторинг кода для его последующего асинхронного выполнения и создание событийного цикла.
** ИСХОДНЫЙ КОД **
Основных проектов доступен в Patreon:
/ iskhodnyi-kod-26640469
***
🔷 Для донатов. Всегда очень признателен за это:
www.donational...
Весь плейлист:
Основы асинхронности в Python #1: Введение
• Основы асинхронности в...
Основы асинхронности в Python #2: Асинхронность с простыми функциями. Событийный цикл.
• Основы асинхронности в...
Основы асинхронности в Python #3: Асинхронность на колбэках.
• Основы асинхронности в...
Основы асинхронности в Python #4: Генераторы и событийный цикл Round Robin
• Основы асинхронности в...
Основы асинхронности в Python #5: Асинхронность на генераторах
• Основы асинхронности в...
Основы асинхронности в Python #6: Корутины и yield from
• Основы асинхронности в...
Основы асинхронности в Python #7: Asyncio, async/await
• Основы асинхронности в...
"Он будет настолько простым, что, мне кажется, создать что нибудь ещё проще будет не так то уж просто."©
Как же круто! Просто находка, супер материал! С самого начала смотрю канал и каждый раз в восторге
Следующее видео будет опубликовано 8-го декабря в субботу.
Хочу обратить ваше внимание, что в описании к видео есть план серии. И про asyncio и про async/await тоже будет.
Отпишите пожалуйста можно ли использовать асинхронность при парсинге сайтов и записи данных при парсинге в файл?
Если можно то опишите коротко как это можно сделать.
Спасибо за отличные уроки!
Асинхронность при парсинге использовать можно. Но нормальные серверы за такое банят, приходится замедлять парсинг.
"Весь Python за 7 легких уроков" - это не ко мне, поэтому описать это коротко не смогу.
@@zaemiel Спасибо за ответ!
Классно, желаю удачи, неиссякаемого энтузиазма и много денег)
@@zaemiel Привет Олег, давно предлагал тебе сделать цикл обучающих видеоуроков по Python 3, с твоей подачей будет топ на ютубе.
Объяснять асинхронность на примере сокетов, где 90% времени и сложности заключена именно в сокетах. Ловко ты это придумал. Молодец.
+
Это все ещё лучшие видео по асинку, по-крайней мере на русском. Потрясающая подача, настолько четкая мысль, что если очень постараться, то можно воспринимать на слух
Хотя конечно мне лично всё ещё сложно))
Большое спасибо, что автор не мычит и не экает. Приятно слушать. Лайк, рекомендую.
Отличное видео!
Только одна маленькая проблема - код 1_select.py падает при закрытии соединения клиентом. Естественно, это незначительная проблема для такой игрушечной программы (к тому же призванной демонстрировать механику), но если вдруг кому-то интересно, в чем дело и как это исправить, то читайте:
Основной цикл в функции event_loop на каждой итерации ждет появления каких-либо данных для чтения в сокетах. Если сокет закрыт, то значение его fd принимает отрицательное значение (в моей системе -1), и при попытке его проверки select выбрасывает ошибку. Чтобы избежать этого, нужно закрытый сокет сразу же убирать из списка to_monitor, например, так:
to_monitor.remove(client_socket)
@@firstandlast4435 питон очень удобно кодирует такое сообщение как пустую байтовую строку (b''), которая в выражении if not request оценивается, как булево False. Так мы и рвем внутренний цикл и переходим в основной, чтобы ждать следующее соединение
@@firstandlast4435 это значит, что я остановил работу клиента. Нажал Ctrl+C
этот код запускали 10 человек)
Олег, спасибо) Ни одни курсы МФТИ так подробно и понятно не объясняют. Думаю, наконец устраню свой пробел в async)
Для проверки готов ли для чтения сокет, можно использовать не только select, как было сказано в видео. Если рассмотреть подход с неблокироющем I/O, то его можно сделать без доп. методов операционки(select), и использовать только сами сокеты.
чувак, очень круто! прям прёт, без шуток
очень, рад , что нашел этот канал - поделился с коллегами на работе
Приятный, спокойный голос и подробные обьяснения! Спасибо!
00:00 Вынос в функции кода предыдущего примера
03:25 Описание переделки в асинхронный код
06:38 Переделка в асинхронный код. Функция select
16:22 Прогон примера
Нет никаких сомнений что этот канал выстрелит! Подача, разбор нюансов, на высоте! Есть ощущение, что Вы можете сделать цельный курс по Python3. С коммерческой точки зрения проекту светит взлет. И как благодарность за Ваш труд задонатил!
Спасибо вам огромное за донаты!
Круто, спасибо! Всё четко и по полочкам
Одно из полезнейших видео, просмотренных мной в жизни
Классно было увидеть и понять имплементацию event loop, на этом принципе построены все браузеры, ну и не только они. Спасибо!
Очень крутой материал. Асинхронность для себя открыл на днях. Сначала непонятно, а потом как все понял :)
Хорошо обьяснил, у тебя единственный понятный курс по этой теме, спасибо
Здравия и благодати!
Олег, это очень, ОЧЕНЬ круто, спасибо!
Гениальное объяснение.
Отлично! Четко, внятно, понятно! СПАСИБО!!!
огромное спасибо за качественную подачу!
единственный вопрос, который остаётся после просмотра - "кто те люди, что ставят дизлайк?"
Дружище, это очень крутой и понятный материал. Просто пушка, так держать!
P.S. Не понимаю почему так мало подписоты и лукасов
тема сложная
Олег, спасибо за труд!
Очень интересно рассказываете!
мне надо несколько раз пересмотреть, в т.ч. и старые ролики, что бы это понять... Спасибо большое за работу.
При закрытии сокета ведь нужно чистить список to_monitor?
да, нужно. Я забыл сказать про этот момент. В 3-ем видео будет уже все нормально.
Просто добавьте в блок else функции send_message() перед закрытием сокета:
to_monitor.remove(client_socket)
ни одного канала про питон лучше чем у тебя не нашел
Благодарствую за классное видео!
Очень круто! Системно, лаконично, доступно! Зачем после этих видео люди записывают что-то про основы асинхрона? Можно же смело отправлять сюда.
Спасибо, Олег!
Благодарю за работу и старания!
Обожаю твой канал и жду новый ценный материал!
Спасибо за годноту.
хотел в напомнить про удаление клиентского сокета из списка наблюдения и закрытие серверного, но старшие товарищи уже отписались, да и не это важно в данном уроке.
да, было дело.
Вроде уже удалял сокет из наблюдения, не? Или это было в следующем видео... Не помню.
Офигенно. Спасибо большое за такой крутой контент.
Спасибо. Все понятно объяснено.
все понятно и доступно объяснил. спасибо
Олег Молчанов, Вы определенно шарите !)
Олег, Ты oxyительный! Я твой фанат!
отличное видео! всем рекомендую!
Супер видео. Спасибо!
Спасибо за ваши полезные видео)
Все по полочкам😎
Жду асинхроность видео два. Можно же было вместо сокетов просто запросы или бота телегрмного сделать. Но даже так, спасибо за проделанный труд.
По поводу Дескриптора - тут можно, наверное, для более полного понятия объяснить, что descriptor - получается от слова description (описание).
Таким образом descriptor можно понять как описывающий. А если более точно и по программистски, то как "указатель"... Как переменная указывает на область памяти, так и тут дескриптор указывает на какой-либо файл...
Если я не прав, поправьте...
тяжело для моего мозга, но после стараний все понимаю, спасибо
Вы лучший. Спасибо вам большое.
и я тоже понял, спасибо вам!
Спасибо огромное! Потрясающе))
Большое спасибо за видео!
На Python 3.9 сравнение if sock is server_socket перестало работать, как предполагалось - из-за этого пример не получается запустить.
Я исправил на if id(sock) == id(server_socket), с такой редакцией код заработал.
Windows 10 c ncat
СПАСИБО ТЕБЕ ОГРОМНОЕ!
is это и так сравнение по id
первый раз когда воды нету, топ чел
Олег, большое спасибо вам за видео! Все очень понятно и без воды. Скажите, пожалуйста, а список to_monitor в таком случае не будет переполняться ненужными закрытыми клиентскими сокетами? Может их после .close() удалять из списка по значению?
Спасибо за видео)
У кого проблема: ValueError: file descriptor cannot be a negative integer (-1)
Просто удалите server_socket.close() из send_mess :-)
И??? В идеале, сокет сервера же повиснет, если его не закрыть.
Кагбэ тут не сервер-сокет надо закрывать, а клиент-сокет, а если закрывать client_socket, то его надо удалять из списка to_monitor
Upd: думаю, для того чтобы этой ошибки не было - цикл while в функции event_loop надо, например, обернуть код цикла в конструкцию try - except.
Проверь не забыл ли ты убрать not перед request в блоке if функции send_message
@@RoTor_ExСпасибо, а то я в недоумении был, почему у меня клиент не может соединиться. Оказывается ему на каждой итерации закрывают сокет)
Супер!
Это и есть асинхронность в питоне? Насколько я понял вызовы ЛУУПа выполняются не паралельно а все равно последовательно, просто в бесконечном цикле? Да и обе функции остались блокирующими просто отрабатывают логику и возвращают управление в цикл? А главная блокирующая - ready_to_read = select()
Правильно ли я понимаю, что список ready to read все время пустой, до момента, пока пользователь не напишет сообщение или не подключится?
1 момент. Если пользователь подключается, то серверный сокет становится доступным для чтения и помещается в ready_to_read?
2 момент. Если пользователь отправляет сообщение, то клиентский сокет становится доступным для чтения и помещается в ready_to_read?
Класс, спасибо за объяснение, но есть один момент, если гасишь одного клиента, ложится весь сервак
Почему на 11:46 мы не создаём список to_monitor сразу с объектом server_socket, а добавляем объект в цикле? Вопрос выбора автора или принципиальное правило?
что из себя представляет if __name__ == '__main__' знаете?
@@Hagen11rus функция, выполнение которой начинается, если был запущен именно данный модуль?
Я правильно понял что функция select является блокирующей в event_loop?
Получается, что да
Спасибо , очень профессионально. Если можно чутахи шрифт побольше. На мобильнике тяжковато разглядеть.
Просто смотреть бесполезно, пустая трата времени. Это все нужно прорабатывать.
@@zaemiel Может голос твой приятен человеку вот. Удовольствие своего рода же. Зря ты так.
Огромное спасибо.
Только теперь получается, что асинхронность это большой обман и, на самом деле, это таже последовательность но крупные задачи разбили на мелкие. И результат скармливают по чуть-чуть каждому, чтобы не думали что о них забыли.
асинхронность - это не обман
"асинхронность - это не обман". Выше автор ответил. И он прав, потому что истинной параллельности на одном CPU вообще не существует. CPU, если вы не знали, всегда играет в пошаговую стратегию.
@@garrygaller2853 точнее будет сказать "не существует на одном ядре", но т.к. сейчас много ядер в CPU, то итинная параллельность все таки существует.
Спасибо за видос.
Если честно, не совсем понял, каким образом перенос логики в event_loop меняет суть самих блокирующих функций. По сути, если функция блокирует дальнейшее выполнение кода, программа должна "зависнуть" в ожидании сообщения, но уже не в цикле while, а внутри условного recv. Почему это все-таки не так?
Посидел, обдумал. Достигается за счет того, что блокирующие функции вызываются только в тот момент, когда для них есть действие, которое сразу снимает блокировку, верно?
Получается, уже не блокирующие, т.к вызываются только когда от них есть, что получить
Отчего эта ошибка возникает, когда запускаю ваше приложение?
ValueError: file descriptor cannot be a negative integer(-1)
Жаль я не видел этого видео когда писал игровой сервак на плюсах, понятнее видео по select на русскоязычном ютубе не видел 🔥
Немного непонятно, в каких случаях срабатывает:
``` else:
print("close client socket")
client_socket.close()
```
Думаю не актуально, но для тех кто будет смотреть это видео в будущем, будет полезно. У нас условие if request , если оно будет пустым (просто нажать enter в терминале), тогда подключение закроется
Это уроки для профи !
Материал отличный. Но такой вопрос продублировал код а в итоге
```
ready_to_read, _, _ = select(to_monitoring, [], [])
ValueError: file descriptor cannot be a negative integer (-1)
```
Может быть такое связано с форточкой?
Олег, спасибо за видео, надеюсь будет отдельное видео c использованием async, await?
Если вы загляните в описание к видео, то вы увидите план серии. Да, будет и про async/await.
На самом деле ирония ситуации заключается в том, что это и есть про asyncio и про async/await. Каждое видео в этой серии этому посвящено.
Проблема-то в том, что вы ищите волшебную красную таблетку аля "Весь Python за7 легких уроков", которая позволила бы вам без усилий, за очень короткое время понять эти дико сложные концепции.
Легкого пути нет.
Волшебной таблетки тоже нет.
Все очень долго, скучно и непонятно.
@@zaemiel
Долго? Да, скучно - это кому как. Я думаю кому скучно, те не смотрят и не учатся. А те. кто смотрит, вряд ли им скучно. Мне, например очень даже интересно! Непонятно? - Есть в некоторых местах. Но опять-таки, кто хочет, тот разберется и поймет. Тем более с такой подачей материала !
Олег, спасибо за то, что так доступно и разжовано! Твои видео для меня реально находка...
вопрос по поведению selecta при разрыве одного из соединений в to_monitor. Он начнёт игнорировать невалидный дескриптор или свалится?
Не сразу это понял. Вдруг кому-то это сэкономит время: "готовый к чтению" - это означает, что в нем есть новое что прочитать!
Спасибо
Честно говоря мне многое не понятно. Я так понял, что server_socket, не откроет новое соединение с новым клиентом, пока старое не будет закрыто, в этом и есть суть, почему это блокирующая функция. Так каким же образом server_socket может в этом примере открывать сколько угодно новых соединений.
Или я что-то неправильно понял?🤔
select - это сенсор, блокирующая операция, которая снимает блокировку только после того, как в ней появились сокеты на чтение
select вовзращает ОДИН сокет, а список to_monitor
У вас один сервер и много клиентов)
правильно ли я понимаю, что в to_monitor будут накапливаться и уже закрытые соединения?
Я так и не понял почему в в список to_monitor передаем server_socket
ну хорошо, я понял мы туда передаем объект, у которого есть метод .fileno, но как я должен понять что у объекта есть такой метод, а если нет? А если я просто чат бота делаю, как с помощью селекта запускать функции в ивентлупе?
Спасибо!
а почему if sock is server_socket возвращает True?
Единственная непонятная здесь вещь это select. Ну, ну. С сокетами мы конечно все поняли.
Для новичков очень понятно, проще некуда. Чтобы понять одну хрень автор нагрузил в ролик кучу другой непонятной хрени
Событийный цикл должен быть 1 на всю программу или в каждой её части отдельный? Например если я принимаю запрос от пользователя и прогоняю его через большой набор классов, в которых есть методы, я должен в каждом классе делать подобный event loop? (естественно если методы класса подходят для асинхронного выполнения)
Событийный цикл должен быть только один
А не надо удалять сокеты из to_monitor после закрытия соединения? И закрывать server_socket в конце?
server_socket там 1шт на всех ;)
Это понятно, но я думал, что его перед выходом из программы тоже надо закрывать. Хотя, возможно, из-за 6 строки это не обязательно
закройте, конечно. Но это не основной момент.
Что за конструкция
if sock is server_socket
что за is ?
это равносильно:
if sock == server_socket ?
Похоже, но не равносильно.
is - сравнивает сами объекты, а не значения
@@zaemiel Спасибо! Почитаю в интернете.
Оо видосик)) спасибо
А почему мы в select() передаем клиентский сокет в списке для проверки на чтение? Я почему то был свято уверен, что вы его положите в список для проверки на запись. Ну, дескать, сервер, который исполняет скрипт мониторит свой сокет на чтение, куда прилетит сообщение от пользователя и сокет клиента на возможность записи, ведь серверу нужно записать туда ответ, разве нет?
Нам прилетает задача, которую нужно прочитать, поэтому на чтение
Круто.
Я правильно понял что данный код будет корректно работать только на Unix'ах?
нет, не правильно. Я этого не говорил.
Кто-нибудь может подсказать что эта за ошибка ValueError: file descriptor cannot be a negative integer (-1)
у меня возникла такая же проблема, решения пока что не нашел(
@@mrsanchez860 под client_socket.close() нужно дописать to_monitor.remove(client_socket)
Если на клиенте нажать CTRL-C, то сервер выдает ошибку
Traceback (most recent call last):
File "./1-5_select.py", line 40, in
event_loop()
File "./1-5_select.py", line 31, in event_loop
ready_to_read, _, _ = select(to_monitor, [], [])
ValueError: file descriptor cannot be a negative integer (-1)
Подскажите, пожалуйста, как это исправить.
у тебя в to_monitor остаются клиенты, которые уже отключены
не работает, говорит что не может использовать список с пинус первым индексом
работает. Проверяте ваш код
Класс
Как напёрсточник, хотел про асинхронность, а в итоге два видео без единой строчки асинхронного кода 😂
эх был у меня закрепленный коммент на эту тему и я его изменил. Словом это все про асинхронность и я это наглядно демонстрирую.
Но вы можете отписаться от канала в любой момент и выбрать в меньшке ютуба "Не рекоммендовать с этого канала". Делов-то.
а я нашел этот коммент.
Прочитайте закрепленный комментарий к 4-му видео этой серии:
ruclips.net/video/PjZUSSkGLE8/видео.html
У меня не принял второго клиента, только после отключения первого. Код проверил, как в видео Это ограничения Windows 7 / telnet ?
Нет, у меня всё работает на w7 + telnet. Проверяйте код.
(Уже наверное неактуально, оставляю для тех, у кого такая же проблема)
На Ubuntu/telnet тоже работает
Что за подсветка синтаксиса? Sublime Monokai?
Я использую Atom. И да это немного измененная тема Monokai:
github.com/kevinsawicki/monokai
На самом деле это не в одном потоке выполняется, в нутри select запускаются потоки
тоже самое можно было сделать с помощью threading и самим мониторить сокет на доступность, но тогда магия теряется, а так кручу верчу запуть хочу
Это выполняется в одном потоке. Суть всей серии в том, чтобы не использовать threading.
@@zaemiel А как можно сделать асинхронным код, не используя многопоточность или прерывание?😚 второе современные ос не дадут сделать, только на уровне api ос. Ну а если объективно, то данный пример, который у Вас - он по прежнему содержит блокирующие функции, если увеличить объем recv, и отправлять в ответ клиенту файл например, то он так же будет блокировать всю программу.
Это сработает, даже если одновременно два клиента направят request?
вы видео не смотрели штоль?
@@zaemiel На видео, запросы отправляют 2 клиента, но последовательно. Его интересовало - если одновременно
fire!
Лучше бы вернуть сокет из accept_connection, чем делать сайд эффект на глобальное состояние.
2:30 ачепятка
Кто на Windows 10 запускайте через WSL иначе не будет работать)