python协程超时 [英] Timeout for python coroutines

查看:279
本文介绍了python协程超时的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何使协程停止超时?

我不明白为什么asyncio.wait_for()对我不起作用. 我有这样的一段代码(计划实现telnet客户端的实现):

I don't understand why asyncio.wait_for() doesn't work for me. I have such piece of code (planning to make my implementation of telnet client):

def expect(self, pattern, timeout=20): 
    if type(pattern) == str:
        pattern = pattern.encode('ascii', 'ignore')        
    return self.loop.run_until_complete(asyncio.wait_for(self.asyncxpect(pattern), timeout))

async def asyncxpect(self, pattern): #receives data in a cumulative way until match is found
    regexp = re.compile(b'(?P<payload>[\s\S]*)(?P<pattern>%s)' %pattern)
    self.buffer = b''
    while True:
        # add timeout
        # add exception handling for unexpectedly closed connections
        data = await self.loop.sock_recv(self.sock, 10000) 
        self.buffer += data
        m = re.match(regexp, self.buffer)
        if m:
            payload = m.group('payload')
            match = m.group('pattern')
            return payload, match 

正如我认为的那样,这段代码在某个时刻(在await语句中)将控制权返回给事件循环.我认为应该在没有更多数据要接收时发生. 如果事件循环具有控制权,则它可以随着超时而停止.

As I thought this code, at some point (in await statement) returns control to event loop. I thought it should happen when there is no more data to receive. And if event loop has control, it can stop with timeout.

但是,如果服务器没有发送任何有用的(匹配的)内容,我的代码就会在此循环中迷路,就在等待点.

But if server doesn't send anything useful (that matched) my code just stumbles in this loop, right at await point.

我认为此问题与 Python异步强制超时不同,因为我是不使用诸如time.sleep(n)之类的阻塞语句.

I think it is different from this problem Python asyncio force timeout, because I'm not using blocking statements like time.sleep(n).

这是我的代码

推荐答案

服务器关闭连接时,sock_recv返回一个空字节数组(b''),表示文件结束.由于您不处理这种情况,因此您的代码最终陷入了处理相同缓冲区的无限循环中.

When the server closes the connection, sock_recv returns an empty bytearray (b''), indicating end of file. Since you don't handle that condition, your code ends up stuck in an infinite loop processing the same buffer.

要更正它,请添加以下内容:

To correct it, add something like:

if data == b'':
    break

...在data = await loop.sock_recv(...)行之后.

但是以上内容仍无法解释为什么wait_for无法取消恶意协程.问题是await并不意味着将控制权传递给事件循环",这有时会被理解.它的意思是从提供的等待对象中请求值,并在事件对象是否(只要)指示它尚未准备好值的情况下,将控制权交给事件循环." if 是至关重要的:如果对象确实在第一次被问到时准备好一个值,则该值将立即使用,而不会延迟事件循环.换句话说,await 不保证事件循环有运行的机会.

But the above still doesn't explain why wait_for is unable to cancel the rogue coroutine. The problem is that await doesn't mean "pass control to the event loop", as it is sometimes understood. It means "request value from the provided awaitable object, yielding control to the event loop if (and as long as) the object indicates that it does not have a value ready." The if is crucial: if the object does have a value ready when first asked, this value will be used immediately without ever deferring to the event loop. In other words, await doesn't guarantee that the event loop will get a chance to run.

例如,以下协程完全阻塞了事件循环,并阻止了其他协程的运行,尽管它的内循环由等待的所有东西组成:

For example, the following coroutine completely blocks the event loop and prevents any other coroutine from ever running, despite its inner loop consisting of nothing but awaiting:

async def busy_loop():
    while True:
        await noop()

async def noop():
    pass

在您的示例中,由于套接字在文件结束时根本不会阻塞,因此协程永远不会挂起,并且(与上述错误共同作用)协程永远不会退出.

In your example, since the socket does not block at all when it is at end-of-file, the coroutine is never suspended, and (in collusion with the above bug) your coroutine never exits.

为确保其他任务有运行的机会,可以在循环中添加await asyncio.sleep(0).对于大多数代码而言,这不是必需的,因为在这些情况下,请求IO数据将很快导致等待,这时将触发事件循环.(实际上,经常需要这样做表明存在设计缺陷.)在这种情况下,仅与EOF处理错误结合在一起,使代码卡住.

To ensure that other tasks get a chance to run, you can add await asyncio.sleep(0) in a loop. This should not be necessary for most code, where requesting IO data will soon result in a wait, at which point the event loop will kick in. (In fact, needing to do so often indicates a design flaw.) In this case it is only in combination with the EOF-handling bug that the code gets stuck.

这篇关于python协程超时的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆