communicate()和wait()方法不接受超时参数:使用wait_for()函数
使用communicate()很容易使用wait_for()实现超时,但是我无法找到从中断的communicate()调用中检索部分结果的方法,随后对communicate()的调用也不会返回丢失的部分。
示例脚本:
#! /usr/bin/env python3
import asyncio
async def communicate_short(loop):
p = await asyncio.create_subprocess_exec('ping', '127.0.0.1', '-n', '4', stdout=asyncio.subprocess.PIPE)
# For Linux: use '-c' instead of '-n'
try:
# 2 seconds timeout
res = await asyncio.wait_for(p.communicate(), 2)
except asyncio.TimeoutError as e:
# After timeout happens:
# How do I get the subprocess's STDOUT up to this point?
try:
print(res[0].decode('utf-8'))
# Will raise NameError since the communicate() call did not complete
except NameError as e:
print('NameError: %s' % e)
res = await p.communicate()
print(res[0].decode('utf-8'))
# Only prints the later half of ping's STDOUT
if __name__ == '__main__':
loop = asyncio.ProactorEventLoop()
asyncio.set_event_loop(loop)
# For Linux: just do loop = asyncio.get_event_loop()
loop.run_until_complete(communicate_short(loop))示例脚本的输出:
NameError: local variable 'res' referenced before assignment
Reply from 127.0.0.1: bytes=32 time<1ms TTL=128
Reply from 127.0.0.1: bytes=32 time<1ms TTL=128
Ping statistics for 127.0.0.1:
Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
Minimum = 0ms, Maximum = 0ms, Average = 0ms注意,只打印了最后两个数据包。有关前两个数据包的输出丢失。
那么,在超时发生之前,我应该如何从子进程获得输出?
编辑:更准确地说,理想的情况是,我正在寻找的东西:
communicate()所做的工作,即异步写入子进程的STDIN并读取其STDOUT和STDERR,而不可能发生死锁(医生们不祥地警告);看起来这样的东西还不存在,我们必须实现它。
发布于 2017-03-07 08:57:49
对于问题的第二部分,“在超时发生之前,我应该如何从子进程获得输出?”我建议使用asyncio.wait(),它不取消任务(p.communicate()),而使用asyncio.wait_for() (取消任务):
task = asyncio.Task(p.communicate())
done, pending = await asyncio.wait([task], timeout=2)
if pending:
print("timeout!", task._state)
res = await task # Note: It is OK to await a task more than once
print(res[0].decode())关于“检索部分结果”,我建议不要使用调用communicate()的stdout.read(),而是使用另一种方法:
import asyncio
async def ping(loop, host):
p = await asyncio.create_subprocess_exec(
'ping', host, '-c', '4',
stdout=asyncio.subprocess.PIPE, loop=loop)
async for line in p.stdout:
print(host, "==>", line.decode(), end="")
print(host, "done")
if __name__ == '__main__':
loop = loop = asyncio.get_event_loop()
asyncio.set_event_loop(loop)
tasks = [
ping(loop, '8.8.8.8'),
ping(loop, '127.0.0.1'),
ping(loop, 'example.com'),
]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()结合这两种解决方案(并使用readline()而不是更酷的异步for)提供:
import asyncio
async def ping(loop, host):
p = await asyncio.create_subprocess_exec(
'ping', host, '-c', '10',
stdout=asyncio.subprocess.PIPE, loop=loop)
n = 0
while True:
n += 1
task = asyncio.Task(p.stdout.readline())
done, pending = await asyncio.wait([task], timeout=1)
if not done:
print(host, n, "==>", "Timeout!")
line = await task
if not line:
break
print(host, n, "==>", line.decode(), end="")
print(host, "==>", "done")
if __name__ == '__main__':
loop = loop = asyncio.get_event_loop()
asyncio.set_event_loop(loop)
tasks = [
# ping(loop, '8.8.8.8'),
# ping(loop, '127.0.0.1'),
ping(loop, 'example.com'),
]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()注意,每行超时(1秒)。
发布于 2017-07-11 05:31:35
如果期望在超时后终止子进程,则可以获得如下部分输出:
future = asyncio.ensure_future(p.communicate())
done, pending = await asyncio.wait([future], timeout=2)
if pending:
# timeout
if p.returncode is None:
# kill the subprocess, then `await future` will return soon
try:
p.kill()
except ProcessLookupError:
pass
output, err = await future
print(output.decode('utf-8'))发布于 2017-03-07 07:26:38
https://stackoverflow.com/questions/42639984
复制相似问题