【发布时间】:2016-01-26 12:11:06
【问题描述】:
我正在用 Python 开发一个进程调度程序。这个想法是从主函数创建几个线程,并在每个线程中启动一个外部进程。外部进程应该继续运行,直到它完成或主线程决定停止它(通过发送终止信号),因为超出了进程的 CPU 时间限制。
问题是有时Popen 调用会阻塞并且无法返回。此代码在我的系统(Ubuntu 14.04.3 LTS)上以约 50% 的概率重现该问题:
import os, time, threading, sys
from subprocess import Popen
class Process:
def __init__(self, args):
self.args = args
def run(self):
print("Run subprocess: " + " ".join(self.args))
retcode = -1
try:
self.process = Popen(self.args)
print("started a process")
while self.process.poll() is None:
# in the real code, check for the end condition here and send kill signal if required
time.sleep(1.0)
retcode = self.process.returncode
except:
print("unexpected error:", sys.exc_info()[0])
print("process done, returned {}".format(retcode))
return retcode
def main():
processes = [Process(["/bin/cat"]) for _ in range(4)]
# start all processes
for p in processes:
t = threading.Thread(target=Process.run, args=(p,))
t.daemon = True
t.start()
print("all threads started")
# wait for Ctrl+C
while True:
time.sleep(1.0)
main()
输出表明只返回了 3 个Popen() 调用:
Run subprocess: /bin/cat
Run subprocess: /bin/cat
Run subprocess: /bin/cat
Run subprocess: /bin/cat
started a process
started a process
started a process
all threads started
但是,运行ps 表明所有四个进程实际上都已启动!
使用 Python 3.4 时没有出现问题,但我想保持 Python 2.7 的兼容性。
编辑:如果我在启动每个后续线程之前添加一些延迟,问题也会消失。
编辑 2:我做了一些调查,阻塞是由 subprocess.py 模块中的第 1308 行引起的,它试图从父进程中的管道中进行一些读取:
data = _eintr_retry_call(os.read, errpipe_read, 1048576)
【问题讨论】:
-
我可以重现您描述的行为。但是,您的示例中存在一些问题:首先,在进入 while 循环之前添加一个输出,并在
Popen()之后立即添加。这不会改变任何东西,但会使它更清晰一些。然后,可以访问Process.counter,这可能会导致竞争条件,因此请删除它,但这也不会改变问题。然后,肯定有一件事是错误的,那就是finally子句中的return retcode,因为finally子句即使在您return时也会执行,但仍然没有变化。有趣的问题! -
@UlrichEckhardt 我根据您的建议更新了代码,最后添加了更多信息。
标签: python multithreading python-2.7 process