Python异步运行事件循环一次吗?
问题描述
我正在尝试理解异步库,特别是使用套接字。我写了一些代码,试图获得理解,
我想异步运行发送器套接字和接收器套接字。我把所有的数据都发送到了最后一个,但是之后我必须再运行一次循环。看一下如何做到这一点,我发现了this link from stackoverflow,我在下面实现了它--但是这里发生了什么呢?有没有比先调用stop
再调用run_forever
更好/更明智的方法呢?
事件循环中stop()
的文档为:
停止运行事件循环。
在调用stop()之前计划的每个回调都将运行。在调用stop()之后计划的回调将不会运行。但是,如果稍后再次调用run_ever(),则将运行这些回调。
和run_forever()
的文档为:
运行到调用Stop()为止。
问题:
- 为什么
run_forever
是通向run_once
的唯一途径?这根本没有意义 - 有没有更好的方法?
- 我的代码看起来像是使用异步库进行编程的合理方式吗?
- 除了
asyncio.async()
之外,是否还有更好的方法将任务添加到事件循环?loop.create_task
在我的Linux系统上显示错误。
https://gist.github.com/cloudformdesign/b30e0860497f19bd6596
解决方案
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中添加的,所以如果您有更早的版本,它将不存在。
相关文章