python 2.4后引入新的模块subprocess,用于取代原有的commands模块。利用subprocess可以非常方便地跑多个后台任务,例如下面的示例代码(省去所有的错误处理):
from subprocess import Popen, PIPE import time procs = [] for cmd in cmd_list: p = Popen(cmd,stdout=PIPE,stderr=PIPE, shell=True) procs.append(p) all_procs = len(procs) done_procs = 0 while done_procs < all_procs: time.sleep(0.1) for p in procs: if p.poll(): done_procs += 1 #do something done this process else: continue
有了subprocess之后,生活变得更加美好了,但是,上面的代码一不小心就会让你掉入难以追查的坑中,subprocess调用的子进程可能hang住,而poll会永远无法等到子进程结束。Complete deadlock!
经过多次试错后发现,cmd命令输出(标准或者错误)小于65536字节时,程序运行完全正常,当输出大于等于65536后,程序立马hang住,看来问题就可能出在输出过多数据上。
查看python的源码发现,如果设置了stdout或stderr,subprocess就会调用os.pipe创建一个管道用于其和子进程之间的通信,而上面的问题正好是cmd输出的数据把pipe塞满,无法继续往pipe里写入数据导致程序hang住,而我们没有去读出pipe数据,而是死等子进程完成,导致死锁。
解决上面的问题可以有两种简单的方法:
1、使用文件代替PIPE,解除PIPE大小的限制:
from subprocess import Popen, PIPE import time procs = [] for cmd in cmd_list: fdout = open(len(procs)+".out", 'w') fderr = open(len(procs)+".err", 'w') p = Popen(cmd,stdout=fdout,stderr=fderr, shell=True) procs.append([p,fdout,fderr]) all_procs = len(procs) done_procs = 0 while done_procs < all_procs: time.sleep(0.1) for p in procs: if p[0].poll(): done_procs += 1 #do something done this process else: continue
2、使用communicate及时读出pipe中内容,避免堵死,但在输出量非常大的情况下会影响性能:
from subprocess import Popen, PIPE import time procs = [] for cmd in cmd_list: p = Popen(cmd,stdout=PIPE,stderr=PIPE, shell=True) procs.append([p, '', '']) all_procs = len(procs) done_procs = 0 while done_procs < all_procs: time.sleep(0.1) for p in procs: if p[0].poll(): done_procs += 1 #do something done this process else: out, err = p[0].communicate() p[1] += out p[2] += err
转载请注明:爱开源 » 小心subprocess的PIPE卡住你的python程序