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.

问题:

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,这会引发 _StopErrorrun_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 中添加的,所以如果你有更早的版本,它就不会存在。