如何为Python subprocess设置超时时间以终止挂起的学生程序?
解决subprocess.Popen读取输出时的超时挂起问题
问题分析
你当前的代码存在两个核心问题:
- 未关闭
stdin,若学生程序等待更多输入,会一直阻塞,导致stdout无输出,主线程永久卡在读取循环中。 - 单独读取
stdout可能引发死锁(比如stderr缓冲区填满后,进程无法继续输出stdout)。
方案1:正确使用communicate()(推荐)
communicate(timeout=10)仅会在进程超时未结束时终止它,正常完成的程序不会被中断。它还能自动处理输入输出的缓冲区问题,避免死锁。
修改后的代码:
import subprocess import sys proc = subprocess.Popen( [sys.executable, arg_path], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True ) # 将所有输入拼接后一次性发送 input_data = "\n".join(arg_list) + "\n" sample_output = "" try: # 等待进程结束,超时10秒 stdout, stderr = proc.communicate(input=input_data, timeout=10) sample_output = stdout except subprocess.TimeoutExpired: # 超时后终止进程并读取剩余输出 proc.kill() stdout, stderr = proc.communicate() sample_output = stdout print("学生程序超时,已终止") # 后续可对比sample_output与预期输出
方案2:线程+进程等待超时
若需要保持逐行读取输出的逻辑,可通过线程读取stdout,主线程监控进程运行时间:
import subprocess import sys import threading proc = subprocess.Popen( [sys.executable, arg_path], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, bufsize=0 ) # 发送所有输入后关闭stdin,告知进程无更多输入 for the_arg in arg_list: proc.stdin.write(f"{the_arg}\n") proc.stdin.close() # 线程函数:逐行读取stdout sample_output = [] def read_stdout(): for line in proc.stdout: sample_output.append(line) stdout_thread = threading.Thread(target=read_stdout) stdout_thread.start() # 等待进程结束,超时10秒 try: proc.wait(timeout=10) stdout_thread.join() # 等待线程完成读取 except subprocess.TimeoutExpired: proc.kill() stdout_thread.join() print("学生程序超时,已终止") sample_output = "".join(sample_output)
关键注意点
- 必须关闭
stdin:否则学生程序可能因等待输入一直阻塞,无法正常退出。 - 避免单独读取
stdout/stderr:单独读取某一个流可能导致缓冲区填满,引发进程死锁。
内容的提问来源于stack exchange,提问作者Just a question
相关产品推荐
相关产品推荐

