2015-04-06 24 views
8

Próbuję zaimplementować prosty pomysł przekazując dane ze standardowego wejścia do współprogram:Python asyncio: czytnik zwrotnego i współprogram komunikacji

import asyncio 
import sys 

event = asyncio.Event() 

def handle_stdin(): 
    data = sys.stdin.readline() 
    event.data = data # NOTE: data assigned to the event object 
    event.set() 

@asyncio.coroutine 
def tick(): 
    while 1: 
     print('Tick') 
     yield from asyncio.sleep(1) 

     if event.is_set(): 
      data = event.data # NOTE: data read from the event object 
      print('Data received: {}'.format(data)) 
      event.clear() 

def main(): 
    loop = asyncio.get_event_loop() 
    loop.add_reader(sys.stdin, handle_stdin) 
    loop.run_until_complete(tick())  

if __name__ == '__main__': 
    main() 

Ten kod działa poprawnie jednak uproszczoną wersję nim ze zmienną zamiast obiektu Event działa zbyt:

data = None 

def handle_stdin(): 
    global data 
    data = sys.stdin.readline() 

@asyncio.coroutine 
def tick(): 
    while 1: 
     print('Tick') 
     yield from asyncio.sleep(1) 

     global data 
     if data is not None: 
      print('Data received: {}'.format(data)) 
      data = None 

moje pytania są następujące: jest podejście z Event prawidłowe? Czy jest lepszy sposób, aby inne obiekty asyncio poradziły sobie z takim problemem? Następnie, jeśli podejście z Event jest w porządku, czy użycie zmiennej jest również w porządku?

Dziękuję.

Odpowiedz

12

myślę asyncio.Queue jest znacznie lepiej nadaje się do tego rodzaju/relacji konsumenta producent:

import asyncio 
import sys 

queue = asyncio.Queue() 

def handle_stdin(): 
    data = sys.stdin.readline() 
    asyncio.async(queue.put(data)) # Queue.put is a coroutine, so you can't call it directly. 

@asyncio.coroutine 
def tick(): 
    while 1: 
     data = yield from queue.get() 
     print('Data received: {}'.format(data)) 

def main(): 
    loop = asyncio.get_event_loop() 
    loop.add_reader(sys.stdin, handle_stdin) 
    loop.run_until_complete(tick())  

if __name__ == '__main__': 
    main() 

Jest mniej logika zaangażowany niż z Event, co trzeba, aby upewnić się ustawić/wyłączony prawidłowo i nie ma potrzeby, aby sleep, pobudka, kontrola, powrót do trybu uśpienia, pętli, podobnie jak w przypadku zmiennej globalnej. Tak więc podejście Queue jest prostsze, mniejsze i blokuje pętlę zdarzeń mniej niż inne możliwe rozwiązania. Pozostałe rozwiązania są technicznie poprawne pod względem poprawności , pod warunkiem, że będą działać poprawnie (o ile nie wprowadzisz żadnych połączeń yield from wewnątrz, jeśli bloków if event.is_set() i if data is not None:). Są po prostu trochę niezdarne.

+0

Dziękuję bardzo @dano, podejście 'queue' wygląda naprawdę lepiej niż" "zdarzenie" 'one. –

2

Jeśli chcesz poczekać na wydarzenie, prawdopodobnie powinieneś używać Event.wait zamiast odpytywania is_set.

@asyncio.coroutine 
def tick(): 
    while True: 
     yield from event.wait() 
     print('Data received: {}'.format(event.data)) 
     event.clear() 
+0

Prawda, w rzeczywistości nawet prosty "yield from event.wait()" bez pętli powinien wystarczyć. –