python asyncio运行一次事件循环?
Posted
技术标签:
【中文标题】python asyncio运行一次事件循环?【英文标题】:python asyncio run event loop once? 【发布时间】:2015-04-25 17:21:37 【问题描述】:我正在尝试了解 asyncio 库,特别是使用套接字。我写了一些代码试图获得理解,
我想异步运行发送方和接收方套接字。我已经到了将所有数据发送到最后一个的地步,但是我必须再运行一个循环。看看如何做到这一点,我找到了this link from ***,我在下面实现了它——但是这里发生了什么?有没有比调用stop
后跟run_forever
更好/更理智的方法?
事件循环中stop()
的文档是:
停止运行事件循环。
在调用 stop() 之前安排的每个回调都会运行。在调用 stop() 之后安排的回调将不会运行。但是,如果稍后再次调用 run_forever(),这些回调将运行。
而run_forever()
的文档是:
运行直到 stop() 被调用。
问题:
为什么run_forever
是通往run_once
的唯一途径?这甚至没有意义
有没有更好的方法来做到这一点?
我的代码看起来是用 asyncio 库编程的合理方式吗?
除了asyncio.async()
之外,还有没有更好的方法可以将任务添加到事件循环中? loop.create_task
在我的 Linux 系统上出现错误。
https://gist.github.com/cloudformdesign/b30e0860497f19bd6596
【问题讨论】:
【参考方案1】:stop(); run_forever()
技巧之所以有效,是因为 stop
的实现方式:
def stop(self):
"""Stop running the event loop.
Every callback scheduled before stop() is called will run.
Callback scheduled after stop() is called won't. However,
those callbacks will run if run() is called again later.
"""
self.call_soon(_raise_stop_error)
def _raise_stop_error(*args):
raise _StopError
因此,下次事件循环运行并执行挂起的回调时,它将调用_raise_stop_error
,从而引发_StopError
。 run_forever
循环只会在该特定异常时中断:
def run_forever(self):
"""Run until stop() is called."""
if self._running:
raise RuntimeError('Event loop is running.')
self._running = True
try:
while True:
try:
self._run_once()
except _StopError:
break
finally:
self._running = False
因此,通过调度 stop()
然后调用 run_forever
,您最终会运行一次事件循环迭代,然后在遇到 _raise_stop_error
回调时停止。您可能还注意到_run_once
是由run_forever
定义和调用的。您可以直接调用它,但如果没有准备好运行的任何回调,有时会阻塞,这可能是不可取的。我认为目前没有更清洁的方法可以做到这一点 - 该答案由asyncio
贡献者 Andrew Svetlov 提供;他可能会知道是否有更好的选择。 :)
一般来说,您的代码看起来很合理,但我认为您不应该一开始就使用这种run_once
方法。这不是确定性的;如果您的列表较长或系统较慢,则可能需要两次以上的额外迭代才能打印所有内容。相反,您应该只发送一个通知接收器关闭的哨兵,然后等待发送和接收协程完成:
import sys
import time
import socket
import asyncio
addr = ('127.0.0.1', 1064)
SENTINEL = b"_DONE_"
# ... (This stuff is the same)
@asyncio.coroutine
def sending(addr, dataiter):
loop = asyncio.get_event_loop()
for d in dataiter:
print("Sending:", d)
sock = socket.socket()
yield from send_close(loop, sock, addr, str(d).encode())
# Send a sentinel
sock = socket.socket()
yield from send_close(loop, sock, addr, SENTINEL)
@asyncio.coroutine
def receiving(addr):
loop = asyncio.get_event_loop()
sock = socket.socket()
try:
sock.setblocking(False)
sock.bind(addr)
sock.listen(5)
while True:
data = yield from accept_recv(loop, sock)
if data == SENTINEL: # Got a sentinel
return
print("Recevied:", data)
finally: sock.close()
def main():
loop = asyncio.get_event_loop()
# add these items to the event loop
recv = asyncio.async(receiving(addr), loop=loop)
send = asyncio.async(sending(addr, range(10)), loop=loop)
loop.run_until_complete(asyncio.wait([recv, send]))
main()
最后,asyncio.async
是将任务添加到事件循环的正确方法。 create_task
是在 Python 3.4.2 中添加的,所以如果你有更早的版本,它将不存在。
【讨论】:
我想我真的很喜欢对事件循环进行“完全控制”的想法,以便首先了解它是如何工作的。不过,我喜欢使用 SENTINAL,这是有道理的 :)以上是关于python asyncio运行一次事件循环?的主要内容,如果未能解决你的问题,请参考以下文章
asyncio: RuntimeError 这个事件循环已经在运行
corroutine RuntimeError中的Asyncio:没有正在运行的事件循环
可以在不暂停 Python 解释器的情况下在后台运行异步事件循环吗?