Генераторы, используемые для этой цели, часто называются Задачи (среди многих других терминов), и я буду использовать этот термин здесь для ясности. Да, это возможно. На самом деле существует несколько подходов, которые работают и имеют смысл в некоторых контекстах. Тем не менее, ни один (что я знаю) работает без эквивалента по крайней мере для одного из gevent.spawn
и gevent.joinall
. Для более мощных и хорошо продуманных требуется эквивалент для обоих.
Основная проблема заключается в следующем: генераторы могут быть приостановлены (когда они нанесли yield
), но это все. Чтобы снова выключить их, вам понадобится другой код, вызывающий next()
. На самом деле вам даже нужно позвонить next()
только что созданному генератору, чтобы он сделал что-нибудь для начала. Аналогично, сам генератор - не лучшее место, чтобы решить, что следует делать дальше. Итак, вам нужен цикл, который инициирует срез времени каждой задачи (запускает их до следующего yield
) и переключается между ними неограниченное время. Это обычно называют планировщиком. Они, как правило, становятся действительно волосатыми очень быстро, поэтому я не буду пытаться писать полный планировщик в один ответ. Есть, однако, некоторые основные понятия, я могу попытаться объяснить:
- Один обычно лечит
yield
как давать управление обратно Sheduler (действует аналогично gevent.sleep(0)
в коде). Это означает, что генератор делает все, что он хочет сделать, и когда он находится в месте, где контекстный переключатель удобен и, возможно, полезен, он yield
.
- В Python 3.3+,
yield from
- очень полезный инструмент для делегирования другому генератору. Если вы не можете использовать его, вы должны заставить планировщик эмулировать стек вызовов и вернуть значения маршрута в нужное место и сделать что-то вроде result = yield subtasks()
в ваших задачах. Это медленнее, сложнее реализовать и вряд ли даст полезные трассировки стека (yield from
делает это бесплатно). Но до недавнего времени это было лучшее, что у нас было.
- В зависимости от вашего варианта использования вам может потребоваться широкий спектр инструментов для управления задачами. В обычных примерах возникают новые задачи, ожидающие завершения задачи, ожидающие завершения любой из нескольких задач, обнаружение сбоя (неперехваченное исключение) других задач и т. Д. Обычно они обрабатываются планировщиком, а задачам предоставляется API для связи с планировщиком. Оптимальным способом (но не всегда идеальным) для этого сообщения является
yield
ing специальные значения.
- Одно довольно важное различие между задачами генератора и gevent (и аналогичными библиотеками) заключается в том, что контекстные переключатели в последнем неявны, в то время как задачи делают тривиальным определение контекстных переключателей: только то, что
yield [from]
может запускать код планировщика.Например, вы можете убедиться, что кусок кода является атомарным (w.r.t. другие задачи, если вы добавляете потоки в микс, вам приходится беспокоиться о них самостоятельно), просто просматривая код, не проверяя что-либо, которое он вызывает.
Наконец-то, возможно, вас заинтересует tutorial от Greg Ewing по созданию такого планировщика. (Это произошло на python-ideas
, в то время как мозговой штурм над тем, что сейчас является PEP 3156. Эти почтовые потоки также могут представлять интерес для вас, хотя веб-архив не подходит для чтения сотен писем в десятках потоков, написанных полгода назад .)
Очень интересно. Множество вещей, о которых я не знал. Этот учебник тоже кажется приятным. ура – kaiseroskilo