尝试停止事件的循环,但是没有

时间:2018-09-12 09:49:46

标签: python python-3.x python-asyncio

根据我对asyncio的了解,这应该只打印0到4,但是会显示完整的10位数字。

stop_loop协程不应该停止等待事件并在我达到5后取消循环吗?

import asyncio

async def run():
    for i in range(10):
        if i == 5:
            e.set()
        print(i)

async def stop_loop():
    await e.wait()
    l.stop()

e = asyncio.Event()

l = asyncio.get_event_loop()
l.set_debug(True)

l.create_task(stop_loop())
l.create_task(run())

try:
    l.run_forever()
finally:
    l.close()

输出为

machine:programs user$ python3 conditional_stop.py 
0
1
2
3
4
5
6
7
8
9

1 个答案:

答案 0 :(得分:1)

asyncio通过在实现为协程的任务之间切换来工作。协程是合作例程,因为协程偶尔会自愿放弃控制,以使 let asyncio事件循环切换到另一个任务。这与线程不同,在线程中,每个任务都可以并且将被调度程序“随意”中断。

协程程序每次在另一个协程程序上使用await时通常会在涉及某些I / O的点上放弃控制。 I / O速度很慢,并且asyncio事件循环负责监视I / O流中的更改,以便它可以知道哪些任务已准备好再次进行更多工作。

您的问题是您的协程不合作:

async def run():
    for i in range(10):
        if i == 5:
            e.set()
        print(i)

该例程没有await语句,因此它永远不会放弃对事件循环的控制。没有其他协程可以运行。

您可以等待asyncio.sleep()通话:

async def run():
    for i in range(10):
        if i == 5:
            e.set()
        print(i)
        await asyncio.sleep(0.01)  # wait 1/100th of a second

另一种选择是将print(i)调用(这是一个I / O操作)替换为使用无阻塞输出流的调用。如果您不在Windows上,则可以为StreamWriter创建一个sys.stdout异步I / O包装器:

import os
import sys

async def run():
    # create an async writer for sys.stdout
    loop = asyncio.get_event_loop()
    writer_transport, writer_protocol = await loop.connect_write_pipe(
        asyncio.streams.FlowControlMixin, os.fdopen(sys.stdout.fileno(), 'wb'))
    writer = asyncio.streams.StreamWriter(
        writer_transport, writer_protocol, None, loop)

    for i in range(10):
        if i == 5:
            e.set()
        writer.write(b'%d\n' % i)
        await writer.drain()

不幸的是,尚不支持为Windows控制台流创建异步I / O流,请参见Pyton issue #26832,您必须使用线程池执行器。

请注意,即使使用后者的协程,也无法保证 stop协程实际上将在调用e.set()来取消{{1}后尽快运行}之前的读数为10!在处理run()之后,该循环可以自由地将控制权交还给相同的协程。向await writer.drain()流缓冲区写入短行很快,而sys.stdout唯一要做的只是给协程以刷新内部传输缓冲区的时间。在大多数情况下,连续成功地对.drain()进行直接无阻塞写操作并不总是有足够的空间让sys.stdout进入,并且stop_loop()协程会将其所有行都写入了writer传输器。