首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Python3异步: wait_for()与超时通信(),如何获得部分结果?

问Python3异步: wait_for()与超时通信(),如何获得部分结果?
EN

Stack Overflow用户
提问于 2017-03-07 04:02:26
回答 3查看 5.6K关注 0票数 8

Python关于异步- Subprocess的文档说:

communicate()和wait()方法不接受超时参数:使用wait_for()函数

使用communicate()很容易使用wait_for()实现超时,但是我无法找到从中断的communicate()调用中检索部分结果的方法,随后对communicate()的调用也不会返回丢失的部分。

示例脚本:

代码语言:javascript
复制
#! /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))

示例脚本的输出:

代码语言:javascript
复制
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

注意,只打印了最后两个数据包。有关前两个数据包的输出丢失。

那么,在超时发生之前,我应该如何从子进程获得输出?

编辑:更准确地说,理想的情况是,我正在寻找的东西:

  1. 执行communicate()所做的工作,即异步写入子进程的STDIN并读取其STDOUT和STDERR,而不可能发生死锁(医生们不祥地警告);
  2. 具有可配置的总超时,以便当子进程终止或到达超时时,返回到目前为止收到的STDOUT和STDERR。

看起来这样的东西还不存在,我们必须实现它。

EN

回答 3

Stack Overflow用户

发布于 2017-03-07 08:57:49

对于问题的第二部分,“在超时发生之前,我应该如何从子进程获得输出?”我建议使用asyncio.wait(),它不取消任务(p.communicate()),而使用asyncio.wait_for() (取消任务):

代码语言:javascript
复制
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(),而是使用另一种方法:

代码语言:javascript
复制
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)提供:

代码语言:javascript
复制
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秒)。

另见:https://github.com/aio-libs/async-timeout

票数 4
EN

Stack Overflow用户

发布于 2017-07-11 05:31:35

如果期望在超时后终止子进程,则可以获得如下部分输出:

代码语言:javascript
复制
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'))
票数 2
EN

Stack Overflow用户

发布于 2017-03-07 07:26:38

你必须使用process.stdout

代码语言:javascript
复制
data = await process.stdout.readline()

参见用户文档中的示例。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/42639984

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档