必须使用线程有多糟糕?我遇到了很多同样的问题,最终决定使用线程来收集子进程的 stdout 和 stderr 上的所有数据,并将其放入线程安全队列中,主线程可以以阻塞方式读取该队列,而不必担心幕后发生的线程。
目前尚不清楚基于线程和阻塞的解决方案预计会出现什么问题。您是否担心必须使其余代码成为线程安全的?这不应该成为问题,因为 IO 线程不需要与任何其余代码或数据交互。如果您的内存要求非常严格,或者您的管道特别长,那么您可能会对生成如此多的线程感到不高兴。我对你的情况不太了解,所以我不能说这是否可能是一个问题,但在我看来,既然你已经产生了额外的进程,那么与它们交互的一些线程不应该是一个可怕的负担。在我的情况下,我还没有发现这些 IO 线程特别有问题。
我的线程函数看起来像这样:
def simple_io_thread(pipe, queue, tag, stop_event):
"""
Read line-by-line from pipe, writing (tag, line) to the
queue. Also checks for a stop_event to give up before
the end of the stream.
"""
while True:
line = pipe.readline()
while True:
try:
# Post to the queue with a large timeout in case the
# queue is full.
queue.put((tag, line), block=True, timeout=60)
break
except Queue.Full:
if stop_event.isSet():
break
continue
if stop_event.isSet() or line=="":
break
pipe.close()
当我启动子进程时,我这样做:
outputqueue = Queue.Queue(50)
stop_event = threading.Event()
process = subprocess.Popen(
command,
cwd=workingdir,
env=env,
shell=useshell,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
stderr_thread = threading.Thread(
target=simple_io_thread,
args=(process.stderr, outputqueue, "STDERR", stop_event)
)
stdout_thread = threading.Thread(
target=simple_io_thread,
args=(process.stdout, outputqueue, "STDOUT", stop_event)
)
stderr_thread.daemon = True
stdout_thread.daemon = True
stderr_thread.start()
stdout_thread.start()
然后,当我想读取时,我可以阻塞输出队列 - 从它读取的每个项目都包含一个字符串来标识它来自哪个管道,以及来自该管道的一行文本。很少有代码在单独的线程中运行,并且它仅通过线程安全队列与主线程通信(加上一个事件,以防我需要提前放弃)。也许这种方法很有用,可以让您通过线程和阻塞来解决问题,但不必重写大量代码?
(我的解决方案变得更加复杂,因为我有时希望尽早终止子进程,并希望确保线程全部完成。如果这不是问题,您可以摆脱所有 stop_event 内容,它会变得非常简洁。)