Python2中subprocess的timeout支持
在Python中,subprocess
模块用于创建和管理子进程。通过subprocess
模块,我们可以在Python程序中调用其他程序并与其进行交互。然而,在某些情况下,我们可能希望设置一个超时时间,以避免子进程无限期地运行下去,导致不必要的等待。
在Python3.3及之后的版本中,subprocess
模块为run()
方法添加了timeout
参数,用于设置子进程执行的超时时间。但在Python2中,并没有直接提供这样的参数,因此我们需要自己添加超时机制。本文将详细讨论如何在Python2中实现subprocess
的超时支持。
使用signal模块实现超时
一个常见的方法是使用signal
模块来设置超时时间。signal
模块允许我们捕获和处理各种信号,可以用来实现超时机制。
下面是一个示例代码:
import subprocess
import signal
class TimeoutException(Exception):
pass
def run_command(command, timeout):
process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
def kill_process(process):
try:
process.kill()
except OSError:
pass
timer = signal.signal(signal.SIGALRM, lambda signum, frame: kill_process(process))
signal.alarm(timeout) # 设置超时时间
output, error = process.communicate()
signal.alarm(0) # 取消超时
signal.signal(signal.SIGALRM, timer) # 恢复原信号处理程序
if process.returncode == 0:
return output
else:
raise subprocess.CalledProcessError(returncode=process.returncode, cmd=command, output=output, stderr=error)
try:
output = run_command("ls -l", 2) # 超时2秒
print(output)
except TimeoutException:
print("Command timed out")
except subprocess.CalledProcessError as e:
print("Error:", e)
在上面的代码中,我们定义了一个run_command
函数,它接收一个命令和超时时间作为参数。我们通过subprocess.Popen
启动子进程,并使用signal.alarm
设置一个定时器,当超时时间到达时,会发送SIGALRM
信号,进而结束子进程的执行。最终,我们可以捕获TimeoutException
来处理超时的情况。
使用threading模块实现超时
另一种方法是使用threading
模块来实现超时。我们可以创建一个子线程来执行子进程,并在主线程中等待一段时间,若超时则终止子线程。
下面是一个用threading
模块实现超时的示例代码:
import subprocess
import threading
class CommandThread(threading.Thread):
def __init__(self, command):
threading.Thread.__init__(self)
self.command = command
self.process = None
def run(self):
self.process = subprocess.Popen(self.command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
self.process.communicate()
def kill(self):
if self.process:
self.process.kill()
def run_command(command, timeout):
thread = CommandThread(command)
thread.start()
thread.join(timeout)
if thread.is_alive():
thread.kill()
thread.join() # 等待线程结束
raise TimeoutException("Command timed out")
else:
return thread.process.stdout.read()
try:
output = run_command("ls -l", 2) # 超时2秒
print(output)
except TimeoutException as e:
print(e)
在上面的代码中,我们定义了一个CommandThread
类,该类继承自threading.Thread
,用于执行子进程。在run_command
函数中,我们创建一个CommandThread
对象,并启动一个子线程来执行子进程。当超时时间到达时,我们终止子线程,并通过捕获TimeoutException
来处理超时的情况。
测试示例
下面我们来测试一下上述两种方法的效果。我们分别设置一个长时间运行的命令,并为其设置超时时间为2秒,看看是否能够在超时后正确终止子进程。
使用signal模块方式
try:
output = run_command("sleep 5", 2) # 超时2秒
print(output)
except TimeoutException:
print("Command timed out")
except subprocess.CalledProcessError as e:
print("Error:", e)
运行结果:
Command timed out
使用threading模块方式
try:
output = run_command("sleep 5", 2) # 超时2秒
print(output)
except TimeoutException as e:
print(e)
运行结果:
Command timed out
通过以上测试可以看出,无论是使用signal
模块还是threading
模块,我们都成功实现了超时机制,能够在预先设定的超时时间内终止子进程的执行。
总结
在Python2中,我们可以通过signal
模块或threading
模块来为subprocess
添加超时支持。这样一来,我们就能够更加灵活地控制子进程的执行时间,避免不必要的等待。