我一直在尝试编写一个运行子进程的应用程序,并且(除其他外)在GUI中显示它们的输出,并允许用户单击按钮取消它们。我开始这样的过程:
queue = Queue.Queue(500)
process = subprocess.Popen(
command,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
iothread = threading.Thread(
target=simple_io_thread,
args=(process.stdout, queue))
iothread.daemon=True
iothread.start()
其中simple_io_thread的定义如下:
def simple_io_thread(pipe, queue):
while True:
line = pipe.readline()
queue.put(line, block=True)
if line=="":
break
这很好用。在我的UI中,我定期从队列中执行非阻塞“get”。但是,当我想终止子进程时,我的问题就来了。 (子进程是一个任意的进程,而不是我自己编写的。)我可以使用terminate方法来终止进程,但我不知道如何保证我的I / O线程将终止。它通常会在管道上进行阻塞I / O.这可能会或可能不会在我终止流程后的某个时间结束。 (如果子进程产生了另一个子进程,我可以终止第一个子进程,但是第二个进程仍然会保持管道打开。我甚至不确定如何让这些曾经的子进程彻底终止。)之后,I / O线程将尝试将输出排队,但我不想承诺无限期地从队列中读取。
理想情况下,我想要一些方法来请求终止子进程,阻塞一小段(<0.5s)的时间,之后保证I / O线程已退出(或将及时退出)不干涉任何其他事情)并且我可以停止从队列中读取。
对我来说,解决方案使用I / O线程并不重要。如果有另一种方法可以在Windows和Linux上使用Python 2.6和Tkinter GUI,那就没问题。
process.stdout.close()
我明白了:
IOError: close() called during concurrent operation on the same file object.
...在主线程上。如果我在主线程上执行此操作:
os.close(process.stdout.fileno())
我明白了:
close failed in file object destructor: IOError: [Errno 9] Bad file descriptor
...稍后在主线程中尝试关闭文件句柄时。
答案 0 :(得分:2)
我知道这是一个旧帖子,但是如果它仍然可以帮助任何人,我认为你的问题可以通过将subprocess.Popen实例传递给io_thread而不是它的输出流来解决。
如果您这样做,那么您可以将while True:
行替换为while process.poll() == None:
。
process.poll()检查子进程返回码;如果该过程尚未完成,则没有一个(即process.poll() == None
)。然后,您可以取消if line == "": break
。
我在这里的原因是因为我今天写了一个非常相似的剧本,我得到了: -
IOError: close() called during concurrent operation on the same file object.
个错误。
再次,如果它有所帮助,我认为我的问题源于(我的)io_thread做了一些过于高效的垃圾收集,并关闭了我给它的文件句柄(我可能错了,但它现在有效..)我的不同之处在于它不是守护进程,它通过subprocess.stdout迭代,而不是使用while循环..即: -
def io_thread(subprocess,logfile,lock):
for line in subprocess.stdout:
lock.acquire()
print line,
lock.release()
logfile.write( line )
我还应该提一下,我将bufsize参数传递给subprocess.Popen,以便它是行缓冲的。
答案 1 :(得分:2)
这可能已经足够了,但对来自搜索引擎的人来说仍然有用......
它显示消息的原因是在子进程完成后它会关闭文件描述符,因此,守护进程线程(并发运行)将尝试使用那些关闭的描述符来引发错误。
通过在子进程wait()或communic()之前加入线程,方法应该足以抑制错误。
my_thread.join()
print my_thread.is_alive()
my_popen.communicate()
答案 2 :(得分:1)
在终止进程的代码中,您还可以显式os.close()
线程正在读取的管道?
答案 3 :(得分:1)
你应该关闭写管道......但是当你编写代码时,你无法访问它。要做到这一点,你应该
Popen
的{{1}} stdout
来读取行。现在您可以关闭写管道,并且读取线程将正常关闭。
simple_io_thread
现在通过
queue = Queue.Queue(500)
r, w = os.pipe()
process = subprocess.Popen(
command,
stdout=w,
stderr=subprocess.STDOUT)
iothread = threading.Thread(
target=simple_io_thread,
args=(os.fdopen(r), queue))
iothread.daemon=True
iothread.start()
您可以关闭管道,os.close(w)
将关闭,没有任何例外。