2015-04-06 13 views
8

Sto cercando di implementare un semplice idea di passare un dato da stdin ad un coroutine:Python asyncio: lettore di callback e comunicazione coroutine

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

Questo codice funziona benissimo tuttavia una versione, semplificata di esso con una variabile invece di un oggetto Event lavora troppo:

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 

le mie domande sono: è l'approccio con Event corretto? O c'è un modo migliore con un altro oggetto asyncio per gestire questo tipo di problema? Quindi, se l'approccio con Event va bene, l'uso di una variabile va bene?

Grazie.

risposta

12

Credo asyncio.Queue è molto più adatto per questo tipo di produttore rapporto/consumatore:

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

C'è meno logica applicata che con un Event, che è necessario assicurarsi di inserire/disinserire correttamente e non è necessario un sleep, wakeup, check, tornare a dormire, loop, come con la variabile globale. Quindi l'approccio Queue è più semplice, più piccolo e blocca il ciclo degli eventi meno delle altre possibili soluzioni. Le altre soluzioni sono tecnicamente corrette, in quanto funzioneranno correttamente (a condizione che non vengano introdotte chiamate yield from all'interno dei blocchi if event.is_set() e if data is not None:). Sono solo un po 'goffo.

+0

Grazie mille @dano, l'approccio 'queue' sembra davvero migliore dell''evento" 'uno". –

2

Se si desidera attendere un evento, è consigliabile utilizzare Event.wait anziché il polling is_set.

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

Vero, in realtà anche un semplice 'rendimento da event.wait()' senza un ciclo dovrebbe essere sufficiente. –