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

Python asyncio: обратная связь с читателем и связь с сопрограммой

Я пытаюсь реализовать простую идею передачи данных из stdin в сопрограмму:

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

Этот код работает отлично, однако упрощенная версия его с переменной вместо объекта Event тоже работает:

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

Мои вопросы: правилен ли подход с Event? Или есть лучший способ с другими объектами asyncio для решения этой проблемы? Тогда, если подход с Event хорош, использование переменной также хорошо?

Спасибо.

4b9b3361

Ответ 1

Я думаю, что asyncio.Queue гораздо лучше подходит для такого рода отношений между производителем и потребителем:

import asyncio
import sys

queue = asyncio.Queue()

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

async def tick():
    while 1:
        data = await 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()

Здесь задействовано меньше логики, чем с Event, которое необходимо проверить, правильно ли вы установили/сбросили, и нет необходимости в sleep, пробуждении, проверке, возвращении в спящий режим, цикле, как с глобальной переменной. Таким образом, подход с Queue проще, меньше и блокирует цикл обработки событий меньше, чем другие возможные решения. Другие решения технически правильны в том if event.is_set(), что они будут функционировать должным образом (до тех пор, пока вы не if event.is_set() какой-либо yield from вызовов внутри if if event.is_set() и if data is not None: blocks). Они просто немного неуклюжи.

Ответ 2

Если вы хотите дождаться события, вероятно, вы должны использовать Event.wait вместо опроса is_set.

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