asyncio
提供了通过 async/await
创建和管理子进程的API。不同于Python标准库的subprocess
,asyncio
的子进程函数都是异步的,并且提供了多种工具来处理这些函数,这就很容易并行执行和监视多个子进程。
创建子进程的方法主要有两个:
coroutine asyncio.create_subprocess_exec()
coroutine asyncio.create_subprocess_shell()
我们看下这两个函数的源码:
1 | async def create_subprocess_shell(cmd, stdin=None, stdout=None, stderr=None, |
create_subprocess_shell
函数以shell
的方式执行子进程。需要执行的命令参数以字符串的方式输入到cmd
中。这种方式执行子进程的话会开启两个子进程(一个是shell
子进程,一个是真正的子进程)。我在Docker容器中遇见过杀子进程的时候,真正的子进程成功被杀死但是对应的shell
子进程未被杀死的情况。建议使用create_subprocess_exec
。
1 | async def create_subprocess_exec(program, *args, stdin=None, stdout=None, |
create_subprocess_exec
函数的执行特别想我们在shell
窗口输入命令。需要执行的程序为program
,其他的子命令以位置参数的方式传进去。最终生成的子进程也只有一个真正的子进程。
注意一点:使用这种方式的时候,参数一定不能包含引号否则子进程执行不成功
实现一个子进程类 来管理子进程
1 | class SubProcess(): |
上面我们实现了一个具有超时关闭功能的子进程类。
超时关闭功能的实现是通过wait_for
函数来实现的,我们看下源码
1 | async def wait_for(fut, timeout, *, loop=None): |
如果我们没有设置timeout
则会一直等待协程结束,当我们设置了之后就会在到期之后将协程结束调。
主要是通过timeout_handle = loop.call_later(timeout, _release_waiter, waiter)
这块代码段实现。
我们看下具体call_later
执行的内容:
1 | def _release_waiter(waiter, *args): |
上面函数就是把我们正在执行的协程停止,我们看看set_result
函数里面做了什么:
1 | def set_result(self, result): |
一个注意点
当我们使用子进程执行一个耗时较久的命令时候 可能会出现子进程卡住的现象。这是因为子进程产生一些数据,他们会被buffer
起来,当buffer
满了,会写到子进程的标准输出和标准错误输出,这些东西通过管道发送给父进程。当管道满了之后,子进程就停止写入,于是就卡住了,及时取走管道的输出就不会出现阻塞了。或者我们并不需要子进程的输出信息 在创建 子进程的时候
1 | (program, *args, stdin=None, stdout=None,stderr=None,loop=None,limit=streams._DEFAULT_LIMIT, **kwds) |
可以将stdin
stdout
stderr
设置成asyncio.subprocess.DEVNULL
这样直接不再产生到标准输出的东西,管道就不会满了。
推荐文章: