我有一个 Python 模块,它使用 subprocess 模块卸载一些处理。该模块使用 stdin 和 stdout 上的管道通过 subprocess.communicate 方法读取和写入数据。其中一个子进程重新进入 Python 模块并生成另一个子进程。这会使应用程序死锁,因为父子进程正在使用 stdin 和 stdout 文件描述符。
有没有办法避免这种死锁,而不必到处创建和清理临时文件?
这是我的场景的更详细信息:它是一个在 FastCGI 服务器中运行的网络应用程序。当请求 PDF 文件时,会生成一个子进程以启动第三方应用程序 (wkhtmltopdf) 来创建 PDF。然后,该应用程序开始通过我的 FastCGI 模块下载图像——在与 PDF 创建者的父进程相同的进程中。检索图像会通过 subprocess 调用另一个第三方应用程序,这会导致死锁,因为 stdin 和 stdout 已被 PDF 创建者子进程使用。
this blog post 中提到了这个问题(在最后),但没有提供后续解决方案。我可能不得不求助于临时文件,但我更喜欢管道。以前有人遇到过这个问题吗?
请您参考如下方法:
我认为您的分析不正确。
您说进程死锁是因为标准输入和标准输出正在被 PDF 创建者进程使用。但是,由于这是使用 subprocess 模块启动的,因此 PDF 创建者进程的标准输入和标准输出 fds 只是 FastCGI 进程的常规管道 fds。没有理由不能让多个正在进行的子流程“通信”调用同时进行。
但是,请注意“通信”是一个阻塞调用。当您进程中的一个线程正在运行通信时,该线程将无法执行任何其他操作,例如为图像提供 HTTP 请求。
在这种情况下,一种解决方案是让您的服务器成为多线程。我很惊讶它还没有,因为大多数网络服务器可以同时处理多个请求(在不同的线程中),所以这应该“正常工作”。
但也许您在使用“交流”时做错了什么。我写了一个小例子,说明如何在同一进程中同时使用多个正在进行的“通信”调用,也许您可以将其用作解决方案的基础。如果不更多地了解您的问题,就很难提供更好的服务。
import subprocess
from threading import Thread
sp1 = subprocess.Popen(["bash","-c","sleep 2;echo output1"],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,shell=False,close_fds=True)
sp2 = subprocess.Popen(["bash","-c","sleep 1;echo output2"],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,shell=False,close_fds=True)
def readfrom(which,sp):
print "Thread #%d starting."%(which,)
(stdout, stderr) = sp.communicate()
print "Thread #%d finished, output: %s"%(which,stdout)
t1=Thread(target=readfrom,args=(1,sp1))
t2=Thread(target=readfrom,args=(2,sp2))
t1.start()
t2.start()
t1.join()
t2.join()
