2015-05-26 4 views
15

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

Вот реализация нормальных функций:

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() 

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

+1

Почему вы не любите петлю? Код с петлей довольно очевиден и легко читается. –

ответ

15

Если вы действительно хотите исключить цикл 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 будет выйдите сразу после печати сейчас.

+0

Это то, что я хочу. Спасибо! – changyuheng

0

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

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


Во втором примере (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 не может работать, и наоборот.

Программа выполняет так:

  1. петля вводится.
  2. Цикл звонков hello_world.
  3. time.sleep(1) в hello_world. Программа спит в течение одной секунды.
  4. Hello World печатный.
  5. hello_world дает.
  6. Цикл звонков good_evening.
  7. Good Evening печатный.
  8. time.sleep(1) в good_evening. Программа спит в течение одной секунды.
  9. good_evening дает.
  10. Перейдите к 2.

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

+0

Спасибо @uranusjr. Я написал коды, а также запустил его. Я знаю, что поток его, а также знаю, что сон блокирует поток и дает выход из асинхронизации. не. Я не уверен, что путь правильный или путинский. Я знаю концепцию сопрограммы, но не использовал ее раньше. До сих пор я не видел, чтобы другие люди писали один и тот же код для этой цели. Они используют одну из обычных функций и сопрограмму, но не оба. – changyuheng

+0

Сопрограммы верхнего уровня не выполняют асинхронную программу; программа является асинхронной, только если вы используете асинхронные API, такие как 'asyncio.sleep'. Вы можете использовать блокирующие API внутри сопрограмм (например, 'time.sleep'), но блокирующие API блокируют всю программу, включая другие сопрограммы. Является ли это приемлемым, зависит от вашего варианта использования. – uranusjr

+0

Еще раз спасибо. Я обновил вопрос и код. Замените call_soon на call_later и удалите time.sleep(). – changyuheng

0
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 не имеет оптимизации хвостового вызова. Оставьте код здесь как неправильный пример.

+0

Я не буду отмечать это как ответ сразу. Подождите некоторое время для лучшего решения. – changyuheng

+0

На самом деле это не сработает - в итоге вы достигнете максимальной глубины рекурсии, и программа выйдет из строя. – dano

0
# 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() 
+0

Это выглядит намного лучше. Спасибо, Мортен. Протестировал этот код с помощью Python 3.6.1. –

+0

Althouh, из-за ожидания, это закончится из фреймов стека. Было бы лучше «ждать asyncio.sleep (1.0)» добавить coros в качестве фьючерсов в цикл событий. – Goodies