使用标准输入时,Python事件循环无法正常工作

问题描述

我正在试着理解Python的异步操作。我写这段代码只是为了演示清楚概念。

import asyncio
import threading


async def printer(b, a):
    print(b)
    await asyncio.sleep(5)
    print(a)


def loop_runner(loop):
    print('[RUNNING LOOP]')
    loop.run_forever()


if __name__ == '__main__':
    event_loop = asyncio.get_event_loop()
    # run_forever() is blocking. running it from separate thread
    loop_thread = threading.Thread(target=loop_runner, args=(event_loop,))
    loop_thread.start()

    while True:
        before, after = input('Before :'), input('After :')
        event_loop.create_task(printer(before, after))

我从单独的线程运行事件循环,并尝试在运行时创建循环中的任务。但是我不明白为什么这个代码不起作用。它接受输入,但随后转到下一迭代,而不打印printer函数中的任何内容。

令人惊讶的是,如果我不从stdin接受输入,而只是使用这样的硬编码消息

   messages = [('Hello', 'world'), ('Foo', 'bar'), ('Alice', 'Bob')]

    for message in messages:
        before, after = message
        coroutine = printer(f'[ITERATION] {count} [MESSAGE] {before}', f'[ITERATION] {count} [MESSAGE] {after}')
        event_loop.create_task(coroutine)
        count += 1

一切都运行得很好。输出

[RUNNING LOOP]
[ITERATION] 0 [MESSAGE] Hello
[ITERATION] 1 [MESSAGE] Foo
[ITERATION] 2 [MESSAGE] Alice
[ITERATION] 0 [MESSAGE] world
[ITERATION] 1 [MESSAGE] bar
[ITERATION] 2 [MESSAGE] Bob

请使用input

帮助我了解此行为

解决方案

您在第一次设置中使用异步不正确。您应该不需要将其与线程模块一起插入。

我推荐的设置是创建一个异步函数main,它包含一个无限循环,您可以在其中请求输入和创建任务。然后,您可以在声明完事件循环之后从事件循环中运行Main。

请注意,在上面的设置中,等待您在Main内部创建的任务是可选的;因为保证stdout由内核同步(我有70%的把握这是真的),您可以一次有任意多的任务运行Print()。但是,如果您确实在等待任务,则在用户尝试输入时,您的程序将不会打印输出;它将调用Printer(),该函数首先写入标准输出,并且仅在Printer()完成后才请求下一组输入。

希望这回答了您的问题。请参阅下面的文档作为附加资源。

https://docs.python.org/3/library/asyncio-task.html

相关文章