python asyncio 运行 事件循环一次?
python asyncio run event loop once?
我正在尝试了解 asyncio 库,特别是使用套接字。我写了一些代码试图获得理解,
我想 运行 发送方和接收方异步套接字。我已经到了将所有数据发送到最后一个的地步,但是我必须再 运行 一个循环。查看如何执行此操作,我发现 ,我在下面实现了它——但是这里发生了什么?有没有比先调用 stop
再调用 run_forever
更好的 better/more 方法?
事件循环中 stop()
的文档是:
Stop running the event loop.
Every callback scheduled before stop() is called will run. Callbacks scheduled after stop() is called will not run. However, those callbacks will run if run_forever() is called again later.
而run_forever()
的文档是:
Run until stop() is called.
问题:
- 为什么世界上
run_forever
是通往 run_once
的唯一途径?这甚至没有意义
- 有更好的方法吗?
- 我的代码看起来像是使用 asyncio 库进行编程的合理方式吗?
- 除了
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
,您最终会 运行ning 事件循环的一次迭代,然后在它到达 _raise_stop_error
打回来。您可能还注意到 _run_once
是由 run_forever
定义和调用的。您可以直接调用它,但如果没有任何回调准备好 运行,它有时会阻塞,这可能是不可取的。我认为目前没有更简洁的方法来执行此操作 - 该答案由 Andrew Svetlov 提供,他是 asyncio
贡献者;他可能会知道是否有更好的选择。 :)
总的来说,您的代码看起来很合理,但我认为您不应该一开始就使用这种 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 中添加的,所以如果你有更早的版本,它就不会存在。
我正在尝试了解 asyncio 库,特别是使用套接字。我写了一些代码试图获得理解,
我想 运行 发送方和接收方异步套接字。我已经到了将所有数据发送到最后一个的地步,但是我必须再 运行 一个循环。查看如何执行此操作,我发现 stop
再调用 run_forever
更好的 better/more 方法?
事件循环中 stop()
的文档是:
Stop running the event loop.
Every callback scheduled before stop() is called will run. Callbacks scheduled after stop() is called will not run. However, those callbacks will run if run_forever() is called again later.
而run_forever()
的文档是:
Run until stop() is called.
问题:
- 为什么世界上
run_forever
是通往run_once
的唯一途径?这甚至没有意义 - 有更好的方法吗?
- 我的代码看起来像是使用 asyncio 库进行编程的合理方式吗?
- 除了
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
,您最终会 运行ning 事件循环的一次迭代,然后在它到达 _raise_stop_error
打回来。您可能还注意到 _run_once
是由 run_forever
定义和调用的。您可以直接调用它,但如果没有任何回调准备好 运行,它有时会阻塞,这可能是不可取的。我认为目前没有更简洁的方法来执行此操作 - 该答案由 Andrew Svetlov 提供,他是 asyncio
贡献者;他可能会知道是否有更好的选择。 :)
总的来说,您的代码看起来很合理,但我认为您不应该一开始就使用这种 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 中添加的,所以如果你有更早的版本,它就不会存在。