[英]python asyncio run event loop once?
I am trying to understand the asyncio library, specifically with using sockets. 我试图了解asyncio库,特别是使用套接字。 I have written some code in an attempt to gain understanding,
我写了一些代码以试图获得理解,
I wanted to run a sender and a receiver sockets asynchrounously. 我想异步运行发送器和接收器套接字。 I got to the point where I get all data sent up till the last one, but then I have to run one more loop.
我到了最后一个发送所有数据的地步,但是我必须再运行一个循环。 Looking at how to do this, I found this link from stackoverflow , which I implemented below -- but what is going on here?
看看如何做到这一点,我从stackoverflow找到了这个链接 ,我在下面实现了 - 但是这里发生了什么? Is there a better/more sane way to do this than to call
stop
followed by run_forever
? 有没有更好/更理智的方法来做到这一点,而不是调用
stop
后跟run_forever
?
The documentation for stop()
in the event loop is: 事件循环中
stop()
的文档是:
Stop running the event loop.
停止运行事件循环。
Every callback scheduled before stop() is called will run.
调用stop()之前安排的每个回调都将运行。 Callbacks scheduled after stop() is called will not run.
调用stop()后调度的回调将不会运行。 However, those callbacks will run if run_forever() is called again later.
但是,如果稍后再次调用run_forever(),则会运行这些回调。
And run_forever()
's documentation is: 而
run_forever()
的文档是:
Run until stop() is called.
运行直到调用stop()。
Questions: 问题:
run_forever
the only way to run_once
? run_forever
的唯一途径run_once
? This doesn't even make sense asyncio.async()
? asyncio.async()
之外,还有更好的方法将事件添加到事件循环中吗? loop.create_task
gives an error on my Linux system. loop.create_task
在我的Linux系统上出错。 https://gist.github.com/cloudformdesign/b30e0860497f19bd6596 https://gist.github.com/cloudformdesign/b30e0860497f19bd6596
The stop(); run_forever()
stop(); run_forever()
stop(); run_forever()
trick works because of how stop
is implemented: 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
So, next time the event loop runs and executes pending callbacks, it's going to call _raise_stop_error
, which raises _StopError
. 因此,下次事件循环运行并执行挂起的回调时,它将调用
_raise_stop_error
,这会引发_StopError
。 The run_forever
loop will break only on that specific exception: 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
So, by scheduling a stop()
and then calling run_forever
, you end up running one iteration of the event loop, then stopping once it hits the _raise_stop_error
callback. 因此,通过调度
stop()
然后调用run_forever
,您最终会运行事件循环的一次迭代,然后在遇到_raise_stop_error
回调时停止。 You may have also noticed that _run_once
is defined and called by run_forever
. 您可能还注意到
_run_once
是由run_forever
定义和调用的。 You could call that directly, but that can sometimes block if there aren't any callbacks ready to run, which may not be desirable. 您可以直接调用它,但如果没有任何回调准备好运行,有时可以阻止,这可能是不可取的。 I don't think there's a cleaner way to do this currently - That answer was provided by Andrew Svetlov, who is an
asyncio
contributor; 我认为目前没有更清洁的方法 - 答案是由安德
asyncio
提供的,他是一名asyncio
撰稿人; he would probably know if there's a better option. 他可能会知道是否有更好的选择。 :)
:)
In general, your code looks reasonable, though I think that you shouldn't be using this run_once
approach to begin with. 通常,您的代码看起来很合理,但我认为您不应该使用此
run_once
方法开始。 It's not deterministic; 这不是确定性的; if you had a longer list or a slower system, it might require more than two extra iterations to print everything.
如果您有更长的列表或更慢的系统,可能需要两次以上的额外迭代来打印所有内容。 Instead, you should just send a sentinel that tells the receiver to shut down, and then wait on both the send and receive coroutines to finish:
相反,你应该只发送一个告诉接收器关闭的标记,然后等待发送和接收协同程序完成:
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()
Finally, asyncio.async
is the right way to add tasks to the event loop. 最后,
asyncio.async
是向事件循环添加任务的正确方法。 create_task
was added in Python 3.4.2, so if you have an earlier version it won't exist. create_task
是在Python 3.4.2中添加的,所以如果你有一个早期版本它将不存在。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.