在n秒后终止python中的函数调用

时间:2015-01-18 05:40:54

标签: c linux python-2.7 subprocess

我的python代码是这样的:

def a():
    ...  
    ...  
    subprocess.call()  
    ...  
    ...  

def b():  
    ...  
    ...  

等等。

我的任务:
1)如果subprocess.call()在3秒内返回,我的执行应该在subprocess.call()返回的那一刻继续执行 2)如果subprocess.call()在3秒内没有返回,subprocess.call()应该终止,我的执行应该在3秒后继续。
3)在subprocess.call()返回或3秒结束之前,不应再进行执行。

这可以通过线程来完成但是如何?

真实代码的相关部分如下:

...  
cmd = ["gcc", "-O2", srcname, "-o", execname];    
p = subprocess.Popen(cmd,stderr=errfile)//compiling C program  
...  
...  
inputfile=open(input,'w')  
inputfile.write(scanf_elements)  
inputfile.close()  
inputfile=open(input,'r')  
tempfile=open(temp,'w')
subprocess.call(["./"+execname,str(commandline_argument)],stdin=inputfile,stdout=tempfile); //executing C program
tempfile.close()
inputfile.close()  
...  
...  

我正在尝试使用python编译和执行C程序。 当我使用subprocess.call()执行C程序并假设C程序包含无限循环时,则应在3秒后终止subprocess.call()并继续执行程序。我应该能够知道subprocess.call()是否被强制终止或成功执行,以便我可以在下面的代码中打印消息。

后端gcc是linux。

4 个答案:

答案 0 :(得分:1)

  

我的任务:
  1)如果subprocess.call()在3秒内返回,我的   执行应该在subprocess.call()返回的那一刻继续   2)如果   subprocess.call()在3秒内没有返回   应该终止subprocess.call()并执行   3秒后继续   3)直到subprocess.call()返回或3   秒完成后,不应再进行执行。

在* nix上,您可以使用signal.alarm()-based solution

import signal
import subprocess

class Alarm(Exception):
    pass

def alarm_handler(signum, frame):
    raise Alarm

# start process
process = subprocess.Popen(*your_subprocess_call_args)

# set signal handler
signal.signal(signal.SIGALRM, alarm_handler)
signal.alarm(3) # produce SIGALRM in 3 seconds

try:
    process.wait() # wait for the process to finish
    signal.alarm(0) # cancel alarm
except Alarm: # subprocess does not return within 3 seconds
    process.terminate() # terminate subprocess
    process.wait()

这是基于threading.Timer()的便携式解决方案:

import subprocess
import threading

# start process
process = subprocess.Popen(*your_subprocess_call_args)

# terminate process in 3 seconds
def terminate():
    if process.poll() is None:
        try:
            process.terminate()
        except EnvironmentError:
            pass # ignore 

timer = threading.Timer(3, terminate)
timer.start()
process.wait()
timer.cancel()

答案 1 :(得分:1)

最后,以下代码有效:

import subprocess
import threading
import time


def process_tree_kill(process_pid):
    subprocess.call(['taskkill', '/F', '/T', '/PID', process_pid])

def main():
    cmd = ["gcc", "-O2", "a.c", "-o", "a"];  
    p = subprocess.Popen(cmd)
    p.wait()
    print "Compiled"
    start = time.time()

    process = subprocess.Popen("a",shell=True)
    print(str(process.pid))   

    # terminate process in timeout seconds
    timeout = 3 # seconds
    timer = threading.Timer(timeout, process_tree_kill,[str(process.pid)])
    timer.start()

    process.wait()
    timer.cancel()

    elapsed = (time.time() - start)
    print elapsed

if __name__=="__main__":
    main()

答案 2 :(得分:0)

如果您愿意将呼叫转换为Popen构造函数而不是call(运行gcc的方式相同),那么解决此问题的一种方法是等待3秒,轮询子流程,然后根据其returncode属性是否仍为None来执行操作。考虑以下非常人为的例子:

import sys
import time
import logging
import subprocess

logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s', level=logging.INFO)

if __name__ == '__main__':
  logging.info('Main context started')
  procCmd = 'sleep %d' % int(sys.argv[1])
  proc = subprocess.Popen(procCmd.split())

  time.sleep(3)
  if proc.poll() is None:
    logging.warning('Child process has not ended yet, terminating now')
    proc.terminate()
  else:
    logging.info('Child process ended normally: return code = %s' % str(proc.returncode))

  logging.info('Main context doing other things now')
  time.sleep(5)
  logging.info('Main context ended')

这会导致不同的日志记录输出,具体取决于子进程是否在3秒内完成:

$ python parent.py 1
2015-01-18 07:00:56,639 INFO Main context started
2015-01-18 07:00:59,645 INFO Child process ended normally: return code = 0
2015-01-18 07:00:59,645 INFO Main context doing other things now
2015-01-18 07:01:04,651 INFO Main context ended
$ python parent.py 10
2015-01-18 07:01:05,951 INFO Main context started
2015-01-18 07:01:08,957 WARNING Child process has not ended yet, terminating now
2015-01-18 07:01:08,957 INFO Main context doing other things now
2015-01-18 07:01:13,962 INFO Main context ended

请注意,即使子进程比此更快完成,上述方法也将始终等待3秒。您可以将上述内容转换为类似循环的内容,如果您想要不同的行为,则会不断轮询子进程 - 您只需要跟踪已经过了多长时间。

答案 3 :(得分:-1)

#!/usr/bin/python

import thread
import threading
import time
import subprocess
import os 

ret=-1

def b(arg):
    global ret
    ret=subprocess.call(arg,shell=True);

thread.start_new_thread(b,("echo abcd",))
start = time.time()


while (not (ret == 0)) and ((time.time() - start)<=3):
    pass

if (not (ret == 0)) :
    print "failed"
    elapsed = (time.time() - start)
    print elapsed
    thread.exit()

elif (ret == 0):#ran before 3 sec
    print "successful"
    elapsed = (time.time() - start)
    print elapsed

我写了上面的代码,它正在工作并满足我所有的约束。 链接https://docs.python.org/2/library/thread.html说:

thread.exit() 提高SystemExit异常。如果没有捕获,这将导致线程以静默方式退出。

所以我认为不应该存在孤儿进程,资源被阻塞等问题。请建议。