根据我对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
答案 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传输器。