我正在使用python 2.7
并且无法升级或反向移植subprocess32
。我在线程环境中使用它,通常它工作正常,但有时subprocess
创建没有返回,因此线程挂起,即使strace
在挂起的实例中也不起作用,所以我没有得到反馈。
E.G。此行可能导致挂起(返回的数据很小,因此不是管道问题):
process = subprocess.Popen(cmd,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
我后来读到python 2.7中的子进程不是线程安全的,并且在最新版本中修复了“各种问题”。我正在使用多个线程调用子进程。
我用以下代码(作为一个可证明的示例 - 而不是我的实际代码)演示了这个问题,该代码以每个subprocess
启动多个线程:
import os, time, threading, sys
from subprocess import Popen
i=0
class Process:
def __init__(self, args):
self.args = args
def run(self):
global i
retcode = -1
try:
self.process = Popen(self.args)
i+=1
if i == 10:
sys.stdout.write("Complete\n")
while self.process.poll() is None:
time.sleep(1.0)
retcode = self.process.returncode
except:
sys.stdout.write("ERROR\n")
return retcode
def main():
processes = [Process(["/bin/cat"]) for _ in range(10)]
# start all processes
for p in processes:
t = threading.Thread(target=Process.run, args=(p,))
t.daemon = True
t.start()
sys.stdout.write("all threads started\n")
# wait for Ctrl+C
while True:
time.sleep(1.0)
main()
这通常会导致一个或多个subprocess
来电永不返回。有没有人有关于此或解决方案/替代方案的更多信息
我正在考虑使用弃用的commands.getoutput
,但不知道这是否是线程安全的?它似乎对上面的代码正常工作。
答案 0 :(得分:2)
如果您的线程所做的大部分工作只是在等待子进程,那么您可以使用协同程序更有效地实现这一点。使用python2,您可以使用生成器来实现这一点,因此run
函数的必要更改是:
time.sleep(1.0)
替换为yield
,将控件传递给其他例程return retcode
替换为self.retcode = retcode
或类似内容,因为生成器无法返回值before python3.3 然后main
函数可能是这样的:
def main():
processes = [Process(["/bin/cat"]) for _ in range(10)]
#since p.run() is a generator this doesn't run any of the code yet
routines = [p.run() for p in processes]
while routines:
#iterate in reverse so we can remove routines while iterating without skipping any
for routine in reversed(routines):
try:
next(routine) #continue the routine to next yield
except StopIteration:
#this routine has finished, we no longer need to check it
routines.remove(routine)
这是为了给你一个开始的地方,我建议在收益率周围添加print
语句或使用pythontutor来更好地理解执行的顺序。
这样做的好处是永远不会让任何线程等待任何事情,只需要一个线程一次执行一段处理,这比许多空闲线程更有效。