Skip to main content

18.5.6. 子过程

18.5.6.1. Windows事件循环

在Windows上,默认事件循环是不支持子进程的 SelectorEventLoop。应该使用 ProactorEventLoop。在Windows上使用它的示例:

import asyncio, sys

if sys.platform == 'win32':
    loop = asyncio.ProactorEventLoop()
    asyncio.set_event_loop(loop)

18.5.6.2. 创建一个子进程:使用Process的高级API

coroutine asyncio.create_subprocess_exec(*args, stdin=None, stdout=None, stderr=None, loop=None, limit=None, **kwds)

创建子流程。

limit 参数设置传递给 StreamReader 的缓冲区限制。有关其他参数,请参阅 AbstractEventLoop.subprocess_exec()

返回 Process 实例。

此功能是 协同

coroutine asyncio.create_subprocess_shell(cmd, stdin=None, stdout=None, stderr=None, loop=None, limit=None, **kwds)

运行shell命令 cmd

limit 参数设置传递给 StreamReader 的缓冲区限制。有关其他参数,请参阅 AbstractEventLoop.subprocess_shell()

返回 Process 实例。

应用程序有责任确保所有空格和元字符被适当引用以避免 壳注射 漏洞。 shlex.quote() 函数可以用于正确地转义将用于构建shell命令的字符串中的空格和shell元字符。

此功能是 协同

使用 AbstractEventLoop.connect_read_pipe()AbstractEventLoop.connect_write_pipe() 方法连接管道。

18.5.6.3. 创建子进程:使用subprocess.Popen的低级API

使用 subprocess 模块异步运行子进程。

coroutine AbstractEventLoop.subprocess_exec(protocol_factory, *args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs)

从一个或多个字符串参数(编码到 文件系统编码 的字符串或字节字符串)创建子过程,其中第一个字符串指定要执行的程序,其余字符串指定程序的参数。 (因此,字符串参数一起形成程序的 sys.argv 值,假设它是一个Python脚本。)这类似于使用shell = False调用的标准库 subprocess.Popen 类,以及作为第一个参数传递的字符串列表;然而,其中 Popen 采用作为字符串列表的单个参数,subprocess_exec() 采用多个字符串参数。

protocol_factory 必须实例化 asyncio.SubprocessProtocol 类的子类。

其他参数:

  • stdin:表示要使用 connect_write_pipe() 连接到子进程的标准输入流的管道的文件状对象,或常量 subprocess.PIPE (默认值)。默认情况下,将创建和连接新的管道。

  • stdout:表示要使用 connect_read_pipe() 连接到子进程的标准输出流的管道的文件状对象,或常量 subprocess.PIPE (默认值)。默认情况下,将创建和连接新的管道。

  • stderr:表示要使用 connect_read_pipe() 连接到子过程的标准错误流的管道的文件样对象,或者常量 subprocess.PIPE (缺省值)或 subprocess.STDOUT 之一。默认情况下,将创建和连接新的管道。当指定 subprocess.STDOUT 时,子进程的标准错误流将连接到与标准输出流相同的管道。

  • 所有其他关键字参数传递给 subprocess.Popen 而不解释,除了 bufsizeuniversal_newlinesshell,不应该指定它们。

返回一对 (transport, protocol),其中 transportBaseSubprocessTransport 的一个实例。

这种方法是 协同

请参阅参数的 subprocess.Popen 类的构造函数。

coroutine AbstractEventLoop.subprocess_shell(protocol_factory, cmd, *, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs)

使用平台的“shell”语法从 cmd 创建子流程,它是一个字符串或编码为 文件系统编码 的字节字符串。这类似于用 shell=True 调用的标准库 subprocess.Popen 类。

protocol_factory 必须实例化 asyncio.SubprocessProtocol 类的子类。

有关其余参数的更多详细信息,请参阅 subprocess_exec()

返回一对 (transport, protocol),其中 transportBaseSubprocessTransport 的一个实例。

应用程序有责任确保所有空格和元字符被适当引用以避免 壳注射 漏洞。 shlex.quote() 函数可以用于正确地转义将用于构建shell命令的字符串中的空格和shell元字符。

这种方法是 协同

18.5.6.4. 常量

asyncio.subprocess.PIPE

可用作 create_subprocess_shell()create_subprocess_exec()stdinstdoutstderr 参数的特殊值,表示应打开标准流的管道。

asyncio.subprocess.STDOUT

可用作 create_subprocess_shell()create_subprocess_exec()stderr 参数的特殊值,表示标准错误应与标准输出进入相同的句柄。

asyncio.subprocess.DEVNULL

可用作 create_subprocess_shell()create_subprocess_exec()stdinstdoutstderr 参数的特殊值,表示将使用特殊文件 os.devnull

18.5.6.5. 处理

class asyncio.subprocess.Process

create_subprocess_exec()create_subprocess_shell() 函数创建的子过程。

Process 类的API被设计为接近 subprocess.Popen 类的API,但是有一些区别:

这个类是 不是线程安全。另见 子进程和线程 部分。

coroutine wait()

等待子进程终止。设置和返回 returncode 属性。

这种方法是 协同

注解

这将在使用 stdout=PIPEstderr=PIPE 时死锁,子进程为管道生成足够的输出,以阻止等待OS管道缓冲区接受更多数据。使用管道时使用 communicate() 方法来避免这种情况。

coroutine communicate(input=None)

与进程交互:将数据发送到stdin。从stdout和stderr读取数据,直到达到文件结束。等待进程终止。可选的 input 参数应该是要发送到子进程的数据,或 None,如果没有数据应发送到子进程。 input 的类型必须是字节。

communicate() 返回一个元组 (stdout_data, stderr_data)

如果在将 input 写入stdin时引发 BrokenPipeErrorConnectionResetError 异常,则会忽略该异常。它发生在进程退出之前所有数据都写入stdin。

请注意,如果要将数据发送到进程的stdin,则需要使用 stdin=PIPE 创建Process对象。类似地,要在结果元组中获得除 None 以外的任何东西,您还需要给予 stdout=PIPE 和/或 stderr=PIPE

这种方法是 协同

注解

读取的数据在内存中缓冲,因此如果数据大小较大或无限制,则不要使用此方法。

在 3.4.2 版更改: 该方法现在忽略 BrokenPipeErrorConnectionResetError

send_signal(signal)

将信号 signal 发送到子进程。

注解

在Windows上,SIGTERMterminate() 的别名。 CTRL_C_EVENTCTRL_BREAK_EVENT 可以被发送到以包括 CREATE_NEW_PROCESS_GROUPcreationflags 参数开始的过程。

terminate()

停止孩子。在Posix操作系统上,该方法向子进程发送 signal.SIGTERM。在Windows上,调用Win32 API函数 TerminateProcess() 来停止子进程。

kill()

杀死孩子。在Posix操作系统上,函数向子进程发送 SIGKILL。在Windows上 kill()terminate() 的别名。

stdin

标准输入流(StreamWriter),如果过程是使用 stdin=None 创建的,则为 None

stdout

标准输出流(StreamReader),如果过程是使用 stdout=None 创建的,则为 None

stderr

标准错误流(StreamReader),如果过程是使用 stderr=None 创建的,则为 None

警告

使用 communicate() 方法而不是 .stdin.write.stdout.read.stderr.read 来避免由于流暂停读取或写入和阻塞子进程而导致的死锁。

pid

进程的标识符。

请注意,对于由 create_subprocess_shell() 函数创建的进程,此属性是生成的shell的进程标识符。

returncode

退出时的进程的返回代码。 None 值指示进程尚未终止。

负值 -N 表示子节点由信号 N (仅限Unix)终止。

18.5.6.6. 子进程和线程

asyncio支持从不同的线程运行子进程,但有限制:

  • 事件循环必须在主线程中运行

  • 子查看器必须在主线程中实例化,然后才能从其他线程执行子进程。调用主线程中的 get_child_watcher() 函数来实例化子监视器。

asyncio.subprocess.Process 类不是线程安全的。

18.5.6.7. 子过程示例

18.5.6.7.1. 使用传输和协议的子过程

子进程协议的示例,用于获取子进程的输出并等待子进程退出。子过程由 AbstractEventLoop.subprocess_exec() 方法创建:

import asyncio
import sys

class DateProtocol(asyncio.SubprocessProtocol):
    def __init__(self, exit_future):
        self.exit_future = exit_future
        self.output = bytearray()

    def pipe_data_received(self, fd, data):
        self.output.extend(data)

    def process_exited(self):
        self.exit_future.set_result(True)

@asyncio.coroutine
def get_date(loop):
    code = 'import datetime; print(datetime.datetime.now())'
    exit_future = asyncio.Future(loop=loop)

    # Create the subprocess controlled by the protocol DateProtocol,
    # redirect the standard output into a pipe
    create = loop.subprocess_exec(lambda: DateProtocol(exit_future),
                                  sys.executable, '-c', code,
                                  stdin=None, stderr=None)
    transport, protocol = yield from create

    # Wait for the subprocess exit using the process_exited() method
    # of the protocol
    yield from exit_future

    # Close the stdout pipe
    transport.close()

    # Read the output which was collected by the pipe_data_received()
    # method of the protocol
    data = bytes(protocol.output)
    return data.decode('ascii').rstrip()

if sys.platform == "win32":
    loop = asyncio.ProactorEventLoop()
    asyncio.set_event_loop(loop)
else:
    loop = asyncio.get_event_loop()

date = loop.run_until_complete(get_date(loop))
print("Current date: %s" % date)
loop.close()

18.5.6.7.2. 使用流的子过程

示例使用 Process 类来控制子进程和 StreamReader 类从标准输出读取。子过程由 create_subprocess_exec() 函数创建:

import asyncio.subprocess
import sys

@asyncio.coroutine
def get_date():
    code = 'import datetime; print(datetime.datetime.now())'

    # Create the subprocess, redirect the standard output into a pipe
    create = asyncio.create_subprocess_exec(sys.executable, '-c', code,
                                            stdout=asyncio.subprocess.PIPE)
    proc = yield from create

    # Read one line of output
    data = yield from proc.stdout.readline()
    line = data.decode('ascii').rstrip()

    # Wait for the subprocess exit
    yield from proc.wait()
    return line

if sys.platform == "win32":
    loop = asyncio.ProactorEventLoop()
    asyncio.set_event_loop(loop)
else:
    loop = asyncio.get_event_loop()

date = loop.run_until_complete(get_date())
print("Current date: %s" % date)
loop.close()