python 个协程超时
Timeout for python coroutines
如何让协程超时停止?
我不明白为什么 asyncio.wait_for() 对我不起作用。
我有这样一段代码(打算实现我的telnet客户端):
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 语句中)returns 控制事件循环。我认为应该在没有更多数据可接收时发生。
如果事件循环有控制权,它可以超时停止。
但是如果服务器没有发送任何有用的(匹配的)东西,我的代码就会在这个循环中绊倒,就在等待点。
我认为它与这个问题 不同,因为我没有使用像 time.sleep(n).
这样的阻塞语句
当服务器关闭连接时,sock_recv
returns一个空字节数组(b''
),表示文件结束。由于您不处理该情况,您的代码最终会陷入处理同一缓冲区的无限循环。
要更正它,请添加如下内容:
if data == b'':
break
...在 data = await loop.sock_recv(...)
行之后。
但是上面还是没有解释为什么wait_for
无法取消流氓协程。问题是 await
并不像有时理解的那样 "pass control to the event loop"。这意味着 "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." if 是至关重要的:如果对象 确实 在第一次被询问时有一个值准备好,这个值将被立即使用无需遵从事件循环。换句话说,await
不保证事件循环将有机会 运行.
例如,以下协程完全阻塞事件循环并阻止任何其他协程 运行ning,尽管它的内部循环没有任何 但 等待:
async def busy_loop():
while True:
await noop()
async def noop():
pass
在您的示例中,由于套接字在 end-of-file 时根本不会阻塞,协程永远不会挂起,并且(与上述错误串通)您的协程永远不会退出。
为确保其他任务有机会运行,可以在循环中添加await asyncio.sleep(0)
。对于大多数代码来说,这应该不是必需的,请求 IO 数据很快就会导致等待,此时事件循环将启动。(事实上,需要这样做通常表明存在设计缺陷。)在这种情况下它是仅与代码卡住的 EOF-handling 错误结合使用。
如何让协程超时停止?
我不明白为什么 asyncio.wait_for() 对我不起作用。 我有这样一段代码(打算实现我的telnet客户端):
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 语句中)returns 控制事件循环。我认为应该在没有更多数据可接收时发生。 如果事件循环有控制权,它可以超时停止。
但是如果服务器没有发送任何有用的(匹配的)东西,我的代码就会在这个循环中绊倒,就在等待点。
我认为它与这个问题
当服务器关闭连接时,sock_recv
returns一个空字节数组(b''
),表示文件结束。由于您不处理该情况,您的代码最终会陷入处理同一缓冲区的无限循环。
要更正它,请添加如下内容:
if data == b'':
break
...在 data = await loop.sock_recv(...)
行之后。
但是上面还是没有解释为什么wait_for
无法取消流氓协程。问题是 await
并不像有时理解的那样 "pass control to the event loop"。这意味着 "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." if 是至关重要的:如果对象 确实 在第一次被询问时有一个值准备好,这个值将被立即使用无需遵从事件循环。换句话说,await
不保证事件循环将有机会 运行.
例如,以下协程完全阻塞事件循环并阻止任何其他协程 运行ning,尽管它的内部循环没有任何 但 等待:
async def busy_loop():
while True:
await noop()
async def noop():
pass
在您的示例中,由于套接字在 end-of-file 时根本不会阻塞,协程永远不会挂起,并且(与上述错误串通)您的协程永远不会退出。
为确保其他任务有机会运行,可以在循环中添加await asyncio.sleep(0)
。对于大多数代码来说,这应该不是必需的,请求 IO 数据很快就会导致等待,此时事件循环将启动。(事实上,需要这样做通常表明存在设计缺陷。)在这种情况下它是仅与代码卡住的 EOF-handling 错误结合使用。