2015-05-26 11 views
15

Aby zapobiec przełączaniu kontekstów, chcę utworzyć dużą pętlę, która będzie obsługiwać zarówno połączenia sieciowe, jak i niektóre procedury.Jak utworzyć pętlę zdarzeń z toczącymi się coroutines działającymi na niej na zawsze?

Oto realizacja dla normalnych funkcji:

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

Oto realizacja dla współprogram:

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

A mieszane jeden:

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

Jak widać, każda funkcja współprogram ma otoczoną pętlę. Jak mogę zrobić to jak normalne? To znaczy. kiedy to się stanie, wywołaj się po określonym czasie opóźnienia, ale nie po prostu umieść tam pętlę.

+1

Dlaczego nie lubisz pętli? Kod z pętlą jest dość oczywisty i łatwy do odczytania. –

Odpowiedz

15

Jeśli naprawdę chcesz, aby wyeliminować while-pętlę od współprogram (nie jestem pewien, dlaczego uważasz, że jest to konieczne, jest to najbardziej naturalny sposób, aby robić to, co starasz się zrobić), można użyć asyncio.async (lub asyncio.ensure_future na Pythonie 3.4.4+) zaplanować współprogram uruchomić ponownie na następnej iteracji pętli zdarzeń:

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

pamiętać, że trzeba wrócić do używania loop.run_forever() jeśli to zrobić, ponieważ hello_world/good_evening będzie wyjdź natychmiast po wydrukowaniu.

+0

Tego właśnie chcę. Dziękuję Ci! – changyuheng

0

Czy próbowałeś uruchomić trzy podane przykłady? Różnica w zachowaniu jest dość oczywista.

Ponieważ nigdy nie powiedziałeś tego, czego oczekujesz, nie wiadomo, co jest dobre, a co nie. Wszystkie trzy implementacje mogą być dobre lub złe. Mogę powiedzieć, jakie zachowanie ma każda implementacja i dlaczego ma takie zachowanie; tylko ty wiesz, czy to prawda.


W drugim przykładzie (yield from asyncio.sleep(1)), dwa współprogram są prowadzone równocześnie. Oznacza to, że każdy z nich wykona samodzielnie; hello_world drukuje Hello World co sekundę i good_evening drukuje Good Evening co sekundę.

Dwa pozostałe przykłady wykorzystują blokadę time.sleep(1). Oznacza to, że gdy pierwsza funkcja (cokolwiek to jest, powiedzmy, że jest to hello_world) osiągnie time.sleep(1), , cały program zawiesi się na jedną sekundę. Oznacza to, że gdy hello_world zasypia, nie można uruchomić ani jednego i odwrotnie.

Program wykonuje tak:

  1. Pętla zostanie wprowadzony.
  2. Pętla nazywa się hello_world.
  3. time.sleep(1) w hello_world został osiągnięty. Program śpi na jedną sekundę.
  4. Hello World wydrukowane. Wydajności:
  5. hello_world.
  6. Pętla nazywa się good_evening.
  7. Good Evening wydrukowane.
  8. time.sleep(1) w good_evening został osiągnięty. Program śpi na jedną sekundę. Wydajności:
  9. good_evening.
  10. Przejdź do 2.

Dlatego zarówno Hello World i Good Evening pojawiają się co dwa sekund, ponieważ istnieją dwa time.sleep(1) połączeń między poszczególnymi print. Z łatwością zauważysz, że jeśli uruchomisz dwa przykłady.

+0

Dziękujemy @uranusjr. Napisałem kody i uruchomiłem je. Wiem, że jego przepływ, a także wiem, że sen blokowałby wątek i ustępował z asynchronicznego. nie. Nie jestem pewien, czy sposób jest właściwy czy pytonowy. Znam koncepcję coroutine, ale wcześniej jej nie używałem. Do tej pory nie widziałem, aby inni ludzie pisali ten sam kod do tego celu. Używają jednej z normalnych funkcji i korupcji, ale nie obu. – changyuheng

+0

Najlepsi współprawnicy nie tworzą programu asynchronicznego; program jest asynchroniczny tylko wtedy, gdy używasz asynchronicznych API takich jak 'asyncio.sleep'. Można używać blokujących interfejsów API w programach typu coroutines (np. 'Time.sleep'), ale blokujące interfejsy API blokują cały program, w tym inne programy typu coroutines. To, czy jest to dopuszczalne, zależy od twojego przypadku użycia. – uranusjr

+0

Jeszcze raz dziękuję. Zaktualizowałem pytanie i kod. Zamień call_soon na call_later i usuń 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

Kod ten osiągnie maksymalną głębokość rekurencji. Może dlatego, że Python nie ma optymalizacji połączeń końcowych. Pozostaw tutaj kod jako zły przykład.

+0

Nie zaznaczę tego jako odpowiedzi od razu. Zaczekaj trochę czasu na lepsze rozwiązanie. – changyuheng

+0

To w rzeczywistości nie zadziała - w końcu osiągniesz maksymalną głębokość rekursji, a program się zawiesi. – 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

To wygląda o wiele lepiej. Dziękuję, Morten. Przetestowano ten kod za pomocą Pythona 3.6.1. –

+0

Althouh w końcu zabraknie ramek stosu, ponieważ czekają. Lepiej będzie "poczekać na asyncio.sleep (1.0)" dodać coros jako futures do pętli zdarzeń. – Goodies

Powiązane problemy