Python and corutines

Python and corutines

Nikolay, [13.02.17 13:12]

когда питон в рантайме дергает функцию - для этой функции в памяти создается фрейм, в котором уже создаются объекты локальных переменных и всего такого


Nikolay, [13.02.17 13:13]

обычно, когда функция завершает выполнение и возвращает управление через return - фрейм становится ненужен и подбирается GC


Nikolay, [13.02.17 13:14]

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


Nikolay, [13.02.17 13:16]

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


Nikolay, [13.02.17 13:16]

именно это позволяет снаружи управлять цепочкой этих корутин, которые передают друг другу значения, явно переключая контекст


Nikolay, [13.02.17 13:17]

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


Nikolay, [13.02.17 13:18]

ближе к питону - есть две концепции - корутины и эвентлуп


Nikolay, [13.02.17 13:18]

корутин в питоне сейчас два варианта - классические через генератор и yield и “новые” через async/await, которые по сути то же самое, но в красивой обертке и не совсем обратно совместимые (в 3.6 совместимость почти поправили)


Nikolay, [13.02.17 13:20]

эвентлуп же позволяет связать получение событий от системного вызова с обработкой этого события корутиной, то есть мы висим, скажем, на epoll или на файловом хэндлере, получаем от него события и в лупе в зависимости от вида события дергается разная корутина


Nikolay, [13.02.17 13:21]

получается, что выстраивается дерево - в лупе обработка передается корутине, та, в свою очередь, может дернуть еще несколько корутин (раньше - через yield from, сейчас - через await), и так далее.


Nikolay, [13.02.17 13:22]

поэтому async - это просто оператор, показывающий, что функция является корутиной, а await - оператор ожидания переключения контекста обратно из корутины


Nikolay, [13.02.17 13:23]

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


Nikolay, [13.02.17 13:25]

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


Nikolay, [13.02.17 13:27]

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


Nikolay, [13.02.17 13:27]

то есть система сама нам присылает событие, когда надо что-то сделать, и наш код - просто коллбэк на это событие


Nikolay, [13.02.17 13:28]

соответственно, callback hell - это когда колбэков дохрена и они друг друга все дергают и в этом фиг разберешься, но с async/await хоть стало проще понимать, что к чему


Nikolay, [13.02.17 13:34]

ну, работа с корутинами - дело довольно простое. В первую очередь надо создать луп, потом - дерево корутин, которые друг друга ждут через await. И потом вершину дерева через asyncio.ensure_future() запихиваешь в луп и ждешь ее выполнения через loop.run_until_complete()


Nikolay, [13.02.17 13:34]

если у тебя там бесконечный цикл - тогда можно использовать loop.run_forever()


Nikolay, [13.02.17 13:35]

если же хочешь несколько корутин и несколько деревьев ждать сразу - тогда пихаешь все корутины в список и на него делаешь asyncio.gather()


Nikolay, [13.02.17 13:35]

тогда управление вернется только когда все корутины в списке закончат работу

Report Page