【问题标题】:Terminating a program within a time frame through python通过python在时间范围内终止程序
【发布时间】:2017-02-04 00:32:59
【问题描述】:

我正在从 python 脚本运行 fortran 代码,有时需要一段时间才能运行。因此,我使用来自this link 的代码来限制运行时间:

def timeout(func, args=(), kwargs={}, timeout_duration=15, default=1):
    import signal

    class TimeoutError(Exception):
        pass

    def handler(signum, frame):
        raise TimeoutError()

    # set the timeout handler
    signal.signal(signal.SIGALRM, handler) 
    signal.alarm(timeout_duration)
    try:
        result = func(*args, **kwargs)
    except TimeoutError as exc:
        result = default
    finally:
        signal.alarm(0)

    return result

我基本上是在这个(上面)中添加另一个函数(部分如下)来运行 fortran 代码:

subprocess.check_output('./../bin/SPhenoUMSSM ../UMSSM/LH_out_'+mod+' > SPheno_log_'+mod, shell=True)

但是我意识到,当 fortran 代码花费超过 15 秒(这是超时函数的边界)时,它会留在核心并在 for 循环中执行另一个代码,这会在我的核心中创建转储。为了防止这种情况,我想使用subprocess.popen(),因为它给了我 pid 来终止核心中的工作,但我也需要等待进程执行,就像subprocess.check_output() 一样。因此,我想知道是否有一种方法可以将 popen 和 check_output 属性结合起来等到工作在 15 秒内完成,并且它是否不只是终止它。

【问题讨论】:

    标签: python subprocess popen


    【解决方案1】:

    对上面的补充回答,shell也有一个内部超时命令,所以可以如下使用;

    timeout <TIME IN SEC> ./blabla > log_file
    

    我在python中使用如下;

    try:
        check_output('timeout --signal=SIGKILL 12 ./<COMMAND> > log', shell=True)
        flag = 0
    except:
        flag = 1
    

    因此可以检查 flag 是 1 还是 0 以了解作业发生了什么。请注意,--signal=SIGKILL 只是在运行结束时写入Killed,如果它终止的话。更多信号选项可以查看kill -l

    【讨论】:

      【解决方案2】:

      不是世界上最复杂的一段代码,但它可能很有用。

      import subprocess, time
      x = subprocess.Popen(['sleep', '15'])
      polling = None
      i = 0
      while polling == None:
          time.sleep(1)
          polling = x.poll()
          i +=1
          if i > 15: break
      if polling == None:
          try:
              x.kill()
              print "Time out - process terminated" # process terminated by kill command
          except OSError:
              print "Process completed on time" # process terminated between poll and kill commands
          except Exception as e:
              print "Error "+str(e) # kill command failed due to another exception "e"
      else:
          print "Process Completed after "+str(i)+" seconds"
      

      编辑:kill 的问题似乎不起作用。
      尝试使用os.kill(x.pid, signal.SIGKILL) 而不是SIGTERM
      我相信SIGTERM 要求进程干净地关闭,而不是立即终止。不知道是什么驱动了 fortran 脚本,很难知道终止信号的作用。也许代码正在做某事。
      例如:
      如果我按如下方式运行 shell 脚本:

      #!/bin/bash
      trap "echo signal" 15
      sleep 30
      

      并发送它kill -15 pid_number,它不会打印“信号”,直到睡眠在 30 秒后终止,而如果我发出kill -9 pid_number,它会立即终止,没有任何打印输出。

      简短的回答是,我不知道,但我怀疑答案在运行 fortran 代码的脚本中。

      编辑:

      注意:为了成功运行x.kill()os.kill()subprocess.call('kill '+ str(x.pid), shell=True),x 中的shell 选项需要为False。因此可以使用

      import shlex
      args = shlex.split(ARGS HERE) 
      x = subprocess.Popen(args) # shell=False is default
      

      但也请注意,如果您想使用... &gt;&amp; log_file 将输出写入日志文件,它不会起作用,因为&gt;&amp; 不是您的脚本的有效参数,而是您的shell 环境。因此,只需要使用对 python 运行的脚本有效的参数。

      【讨论】:

      • 有趣的是,有时它工作得非常好,但有时它只是将程序留在后台并终止打印。我也尝试了 x.terminate() 但最终得到了相同的结果。你知道为什么会发生吗?
      • 不知道我害怕。该文档没有我可以看到的任何警告。但是我不知道你的 fortran 代码是如何运行的,你的操作系统,或者实际上,你是如何实现上述代码的。我会取出try 语句,看看你是否从kill() 命令中得到错误,它会为你指明正确的方向。
      • 另一个想法,编辑太晚了。在kill() 之后再次尝试poll() 只是为了仔细检查。
      • 我实际上从一开始就没有尝试就运行它,并且没有收到任何错误消息。试过os.kill(PID, signal.SIGTERM)process = psutil.Process(PID) process.terminate(),效果不佳。实际上,我在os.kill 中遇到了关于 str 对象的错误,我不明白它在哪里找到了 str。我的操作系统是 Ubuntu 14.04,python 2.7.6
      • 非常感谢,我试过了,发现问题是由于shell=True选项引起的。 Popen 会终止 shell,但不会终止其中的脚本。因此shell=True 选项需要为 False 才能成功执行这样的命令。但在这种情况下,... &gt; log_file 无法正常工作,可能是因为它不是代码所需的参数。
      【解决方案3】:

      check_output 有超时参数,设置为 15 秒即可。

      try:
        subprocess.check_output(['arg1', 'arg2'], timeout=15)
      except:
        print("Timed out")
      

      这里有文档https://docs.python.org/3/library/subprocess.html#subprocess.check_output

      check_output 也会返回输出,所以如果您关心它,只需存储结果即可。

      还有一个等待功能,可用于更复杂的用例。 check_output 和 wait 都会阻塞,直到进程完成或达到超时。

      【讨论】:

      • 不幸的是,我使用的是 Python 2.7.6,timeout 更新适用于 3.3 版
      • 除非您的公司强迫您这样做,否则您应该停下来,Python 2 已处于 EOL 并将在 2020 年完全死亡。不过,一个简单的 Google 透露了这一点:stackoverflow.com/questions/3733270/python-subprocess-timeout
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-28
      • 2021-09-25
      • 2015-04-21
      相关资源
      最近更新 更多