Подтвердить что ты не робот

Как создать цикл событий с катив-версиями, выполняемыми на нем навсегда?

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

Здесь реализация для нормальных функций:

import asyncio
import time


def hello_world(loop):
    print('Hello World')
    loop.call_later(1, hello_world, loop)

def good_evening(loop):
    print('Good Evening')
    loop.call_later(1, good_evening, loop)

print('step: asyncio.get_event_loop()')
loop = asyncio.get_event_loop()

print('step: loop.call_soon(hello_world, loop)')
loop.call_soon(hello_world, loop)
print('step: loop.call_soon(good_evening, loop)')
loop.call_soon(good_evening, loop)

try:
    # Blocking call interrupted by loop.stop()
    print('step: loop.run_forever()')
    loop.run_forever()
except KeyboardInterrupt:
    pass
finally:
    print('step: loop.close()')
    loop.close()

Здесь реализация для сопрограмм:

import asyncio


@asyncio.coroutine
def hello_world():
    while True:
        yield from asyncio.sleep(1)
        print('Hello World')

@asyncio.coroutine
def good_evening():
    while True:
        yield from asyncio.sleep(1)
        print('Good Evening')

print('step: asyncio.get_event_loop()')
loop = asyncio.get_event_loop()
try:
    print('step: loop.run_until_complete()')
    loop.run_until_complete(asyncio.wait([
        hello_world(),
        good_evening()
    ]))
except KeyboardInterrupt:
    pass
finally:
    print('step: loop.close()')
    loop.close()

И смешанный:

import asyncio
import time


def hello_world(loop):
    print('Hello World')
    loop.call_later(1, hello_world, loop)

def good_evening(loop):
    print('Good Evening')
    loop.call_later(1, good_evening, loop)

@asyncio.coroutine
def hello_world_coroutine():
    while True:
        yield from asyncio.sleep(1)
        print('Hello World Coroutine')

@asyncio.coroutine
def good_evening_coroutine():
    while True:
        yield from asyncio.sleep(1)
        print('Good Evening Coroutine')

print('step: asyncio.get_event_loop()')
loop = asyncio.get_event_loop()

print('step: loop.call_soon(hello_world, loop)')
loop.call_soon(hello_world, loop)
print('step: loop.call_soon(good_evening, loop)')
loop.call_soon(good_evening, loop)
print('step: asyncio.async(hello_world_coroutine)')
asyncio.async(hello_world_coroutine())
print('step: asyncio.async(good_evening_coroutine)')
asyncio.async(good_evening_coroutine())

try:
    loop.run_forever()
except KeyboardInterrupt:
    pass
finally:
    print('step: loop.close()')
    loop.close()

Как вы видите, каждая функция coroutine имеет замкнутый цикл while. Как я могу сделать это как обычный? То есть когда это делается, вызовите себя по истечении заданного времени задержки, но не просто разместите там петлю.

4b9b3361

Ответ 1

Если вы действительно хотите исключить цикл while из сопрограммы (я не уверен, почему вы считаете это необходимым, это самый естественный способ сделать то, что вы пытаетесь сделать), вы можете использовать asyncio.async (или asyncio.ensure_future на Python 3.4.4+), чтобы запланировать повторную копию сопрограммы для следующей итерации цикла событий:

import asyncio

@asyncio.coroutine
def hello_world():
    yield from asyncio.sleep(1)
    print('Hello World')
    asyncio.async(hello_world())

@asyncio.coroutine
def good_evening():
    yield from asyncio.sleep(1)
    print('Good Evening')
    asyncio.async(good_evening())

print('step: asyncio.get_event_loop()')
loop = asyncio.get_event_loop()
try:
    print('step: loop.run_until_complete()')
    asyncio.async(hello_world())
    asyncio.async(good_evening())
    loop.run_forever()
except KeyboardInterrupt:
    pass
finally:
    print('step: loop.close()')
    loop.close()

Обратите внимание, что если вы сделаете это, вам нужно вернуться к использованию loop.run_forever(), так как hello_world/good_evening выйдет сразу после печати. ​​

Ответ 2

Вы действительно попытались запустить три примера, которые вы дали? Разница в поведении довольно очевидна.

Поскольку вы никогда не говорили, чего вы ожидаете, theres не говорит, что правильно, а что нет. Все три реализации могут быть правильными или неправильными. Я могу рассказать вам, какое поведение имеет каждая реализация, и почему это имеет такое поведение; только вы знаете, правильно ли это.


Во втором примере (yield from asyncio.sleep(1)) две сопрограммы запускаются одновременно. Это означает, что каждый будет выполняться сам по себе; hello_world печатает Hello World каждую секунду, а good_evening печатает Good Evening каждую секунду.

В двух других примерах используется time.sleep(1), который блокирует. Это означает, что, когда первая функция (в зависимости от того, что это значит, пусть ее hello_world) достигает time.sleep(1), вся программа будет висеть в течение одной секунды. Это означает, что когда hello_world спит, good_evening тоже не может работать, и наоборот.

Программа выполняется следующим образом:

  • Вводится цикл.
  • Цикл вызывает hello_world.
  • time.sleep(1) в hello_world. Программа спит в течение одной секунды.
  • Hello World.
  • hello_world.
  • Цикл вызывает good_evening.
  • Good Evening.
  • time.sleep(1) в good_evening. Программа спит в течение одной секунды.
  • good_evening.
  • Перейдите к 2.

Поэтому оба Hello World и Good Evening появляются каждые две секунды, потому что между каж- дыми print есть два вызова time.sleep(1). Вы легко заметили бы, что если вы запустите два примера.

Ответ 3

import asyncio


@asyncio.coroutine
def hello_world_coroutine():
    yield from asyncio.sleep(1)
    print('Hello World Coroutine')
    yield from hello_world_coroutine()

@asyncio.coroutine
def good_evening_coroutine():
    yield from asyncio.sleep(1)
    print('Good Evening Coroutine')
    yield from good_evening_coroutine()

print('step: asyncio.get_event_loop()')
loop = asyncio.get_event_loop()
try:
    print('step: loop.run_until_complete()')
    loop.run_until_complete(asyncio.wait([
        hello_world_coroutine(),
        good_evening_coroutine()
    ]))
except KeyboardInterrupt:
    pass
finally:
    print('step: loop.close()')
    loop.close()

UPD

Этот код достигнет максимальной глубины рекурсии. Возможно, Python не имеет оптимизации хвостового вызова. Оставьте код здесь как неправильный пример.

Ответ 4

# asyncio_coroutine_forever.py

import asyncio

async def hello_world():

    await asyncio.sleep(1)
    print('Hello World')
    await good_evening()


async def good_evening():

    await asyncio.sleep(1)
    print('Good Evening')
    await hello_world()


loop = asyncio.get_event_loop()

try:

    loop.run_until_complete(hello_world())
    loop.run_until_complete(good_evening())
    loop.run_forever()

finally:

    print('closing event loop')
    loop.close()