LINUX.ORG.RU

python asyncio.Lock и рекурсия

 


0

2
import asyncio

async def main():
    lock = asyncio.Lock()
    print("Sky")
    async with lock:
        print("Ground")
        async with lock:
            print("Hell")

asyncio.run(main())

Висит вечно, потом Ctrl+C:

 % python3 test.py
Sky
Ground
^CTraceback (most recent call last):
  File "test.py", line 11, in <module>
    asyncio.run(main())
  File "/usr/lib/python3.7/asyncio/runners.py", line 43, in run
    return loop.run_until_complete(main)
  File "/usr/lib/python3.7/asyncio/base_events.py", line 566, in run_until_complete
    self.run_forever()
  File "/usr/lib/python3.7/asyncio/base_events.py", line 534, in run_forever
    self._run_once()
  File "/usr/lib/python3.7/asyncio/base_events.py", line 1735, in _run_once
    event_list = self._selector.select(timeout)
  File "/usr/lib/python3.7/selectors.py", line 468, in select
    fd_event_list = self._selector.poll(timeout, max_ev)
KeyboardInterrupt

https://github.com/python/asyncio/issues/439

В яве такой хрени не встретишь. Но ява старенькая же, а тут новомодная асинхронность, зелёные до тошноты потоки и всё такое. Самое весёлое, что эта зараза даже ошибок не выбрасывает, и отладить такое «зависание» не очень просто.

Deleted

Я бы добавил проверку locked() на lock, ибо объект Lock не reentrant (фиг знает как оно по-русски).
А в Java он как раз reentrant.

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

В этом и заморочка. При том, гвидо по ссылке сказал, что это нафиг не надо.

Тебе есть что возразить? Он ведь прав, в питоне потоке скорее для галочки. А повторный вход для асинхронщины - это такой специфический случай, что никто не собирается о нем париться... кроме тебя и пары человек из багтрекера. Welcome to Python.

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

Он ведь прав, в питоне потоке

То бишь весь asyncio это такая игрушка, и для продакшена негоже оную юзать?

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

Asyncio - это попытка сделать однопоточную поделку хоть как-то юзабельной под нагрузкой. Поскольку питон сам по себе очень медленный, то все функции с питонового кода перекладываются на внешние приложения, как то СУБД, другой сервер, или просто расширения на Си. То есть, по сути питон занимается одним вводом-выводом. Раньше для такого сценария делались многопоточные питоновые приложения, и это работало медленно, потому что сделать эффективный многопоточный диспетчер ввода-вывода весьма непросто, и в том же хаскеле лишь совсем недавно онный доработали до адекватного состояния. Тогда в питоне придумали делать то же, что давным-давно сделали для http-серверов: выкинули к черту все механизмы синхронизации потоков, оставив один поток для событийной обработки ввода-вывода. И о чудо - никаких пятен. Так зачем платить больше?

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

byko3y ★★★★ ()

Это же ересь. Во вложенном async with лок уже захвачен, какой смысл захватывать его второй раз? Для уродов которые не знают что у них в коде происходит в уродоориентированных языках есть reentrant локи, скрывающие логические ошибки, а тут можете либо исправить код, либо написать reentrant лок из 2 строчек, в любом случае пострадаете, заслуженно.

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

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

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

Но вообще, асинхронная архитектура подразумевает снижение уровня вложенности вызовов с переходом на линейную последовательность вызовов.

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

Эта блокировка не поддерживает многопоточность. И в примере всего один поток.

Эм-м-м... так и зачем она нужна? Чтобы было о чем поговорить, попивая пивцо?

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

Для консистентности доступа к обектам, особенно на запись. Не всегда легко разруливать изменения сложных объектов без простой блокировки.

Кроме того, гвидо её создал, значит тоже видит некие кейсы применения. Но оставил грабли.

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

Не всегда легко разруливать изменения сложных объектов без простой блокировки

Делай это синхронно. Без блокировки.

Кроме того, гвидо её создал, значит тоже видит некие кейсы применения

Он сам и пишет, что она нужна для случаев с вызовами параллельных зеленых потоков.

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

Делай это синхронно. Без блокировки.

Кое-кто издевается, как я погляжу 8).

Он сам и пишет, что она нужна для случаев с вызовами параллельных зеленых потоков.

Это и есть тот случай. Технически это один поток, но разные корутины (на которых сделаны зелёные потоки).

Deleted ()

Смотрите ствол. Смотрите нога.

Пыщь! Пыщь!

Не, ну это надо, да? Нога в фарш, клятый ствол!

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

Так это в рамках одного потока работает и на другом уровне абстракции.

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

Технически это один поток, но разные корутины (на которых сделаны зелёные потоки).

Тогда ты изначально играешь с огнем. Я бы делал многошаговый перевод отдельными операциями без блокировок и дополнительные проверки целостности для кода, который будет читать данные между шагами или после ошибки. Если же ты намерен оставлять длинные блокировки, то можешь начинать реализовывать менеджер для разруливания дедлоков - они неизбежно будут возникать, даже после отладки, в продакшене.

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

Но вообще, асинхронная архитектура подразумевает снижение уровня вложенности вызовов с переходом на линейную последовательность вызовов.

По сравнению с чем? С синхронным кодом - конечно же нет, в том то и смысл правильной асинхронщины - структура кода не меняется ровным счётом никак, только добавляются async/await. А про асинхронщину на коллбэках от конченых ублюдков я даже писать не хочу - что угодно будет проще и правильнее неё.

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

Да блин, я это уже обошёл. Там куча способов. Просто весело - создали инструмент, но так, что для практического использования он малополезен, да ещё и опасен (обычный дедлок между потоками хоть по залипшим стекам можно отследить, а тут…).

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

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

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

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

Факт отследить не так уж сложно, но может оказаться «дорого». Суть не в этом, суть в том, что гвидо сказал «ненужно» и на том всё.

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

для практического использования он малополезен, да ещё и опасен

В каком месте это является практическим использованием?

P.S. В доке вообще черным по-белому написано, во что разворачивается такая конструкция, здесь нет какого-то неочевидного поведения.

https://docs.python.org/3/library/asyncio-sync.html#asyncio.Lock

Octagon ()

В яве такой хрени не встретишь

Так ява делалась под индусов.

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