python asyncio运行事件循环一次?

时间:2015-04-25 17:21:38

标签: python sockets asyncsocket python-asyncio

我正在尝试了解asyncio库,特别是使用套接字。我写了一些代码以试图获得理解,

我想异步运行发送方和接收方套接字。我到了最后一个发送所有数据的地步,但是我必须再运行一个循环。看看如何做到这一点,我找到了this link from stackoverflow,我在下面实现了 - 但是这里发生了什么?是否有更好/更理智的方式来调用stop后跟run_forever

事件循环中stop()的文档是:

  

停止运行事件循环。

     

调用stop()之前安排的每个回调都将运行。调用stop()后调度的回调将不会运行。但是,如果稍后再次调用run_forever(),那么这些回调将会运行。

run_forever()的文档是:

  

运行直到调用stop()。

问题:

  • 为什么世界run_foreverrun_once的唯一途径?这甚至没有意义
  • 有更好的方法吗?
  • 我的代码看起来像是使用asyncio库编程的合理方式吗?
  • 除了asyncio.async()之外,还有更好的方法向事件循环添加任务吗? loop.create_task在我的Linux系统上出错。

https://gist.github.com/cloudformdesign/b30e0860497f19bd6596

1 个答案:

答案 0 :(得分:11)

由于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,您最终会运行事件循环的一次迭代,然后在遇到_raise_stop_error回调时停止。您可能还注意到_run_once已定义并调用run_forever。您可以直接调用它,但如果没有任何回调准备好运行,有时可以阻止,这可能是不可取的。我认为目前没有更清洁的方法 - 答案是由安德鲁斯维特洛夫提供的,他是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是向事件循环添加任务的正确方法。在Python 3.4.2中添加了create_task,因此如果您有早期版本,它将不存在。