简体   繁体   English

python asyncio运行事件循环一次?

[英]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: 问题:

  • why in the world is run_forever the only way to run_once ? 为什么在世界上是run_forever的唯一途径run_once This doesn't even make sense 这甚至没有意义
  • Is there a better way to do this? 有一个更好的方法吗?
  • Does my code look like a reasonable way to program with the asyncio library? 我的代码看起来像是使用asyncio库编程的合理方式吗?
  • Is there a better way to add tasks to the event loop besides 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM