Terminating A Function Call In Python After N Seconds
Solution 1:
My task: 1) If subprocess.call() returns within 3 seconds, my execution should continue the moment subprocess.call() returns. 2) If subprocess.call() does not return within 3 seconds, the subprocess.call() should be terminated and my execution should continue after 3 seconds. 3) Until subprocess.call() returns or 3 seconds finishes, the further execution should not take place.
On *nix, you could use signal.alarm()
-based solution:
import signal
import subprocess
classAlarm(Exception):
passdefalarm_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 secondstry:
process.wait() # wait for the process to finish
signal.alarm(0) # cancel alarmexcept Alarm: # subprocess does not return within 3 seconds
process.terminate() # terminate subprocess
process.wait()
Here's a portable threading.Timer()
-based solution:
import subprocess
import threading
# start process
process = subprocess.Popen(*your_subprocess_call_args)
# terminate process in 3 secondsdefterminate():
if process.poll() isNone:
try:
process.terminate()
except EnvironmentError:
pass# ignore
timer = threading.Timer(3, terminate)
timer.start()
process.wait()
timer.cancel()
Solution 2:
Finally the below code worked:
import subprocess
import threading
import time
defprocess_tree_kill(process_pid):
subprocess.call(['taskkill', '/F', '/T', '/PID', process_pid])
defmain():
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()
Solution 3:
If you're willing to convert your call to a Popen
constructor instead of call
(same way you are running gcc
), then one way to approach this is to wait 3 seconds, poll the subprocess, and then take action based on whether its returncode
attribute is still None
or not. Consider the following highly contrived example:
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() isNone:
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')
And this results in different logging output depending upon whether the child process completed within 3 seconds or not:
$pythonparent.py12015-01-18 07:00:56,639INFOMaincontextstarted2015-01-18 07:00:59,645INFO Child process ended normally:returncode=02015-01-18 07:00:59,645INFOMaincontextdoingotherthingsnow2015-01-18 07:01:04,651INFOMaincontextended$pythonparent.py102015-01-18 07:01:05,951INFOMaincontextstarted2015-01-18 07:01:08,957WARNINGChildprocesshasnotendedyet,terminatingnow2015-01-18 07:01:08,957INFOMaincontextdoingotherthingsnow2015-01-18 07:01:13,962INFOMaincontextended
Note that this approach above will always wait 3 seconds even if the subprocess completes sooner than that. You could convert the above into something like a loop that continually polls the child process if you want different behavior - you'll just need to keep track of how much time has elapsed.
Solution 4:
#!/usr/bin/pythonimport thread
import threading
import time
import subprocess
import os
ret=-1defb(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):
passif (not (ret == 0)) :
print"failed"
elapsed = (time.time() - start)
print elapsed
thread.exit()
elif (ret == 0):#ran before 3 secprint"successful"
elapsed = (time.time() - start)
print elapsed
I have written the above code which is working and satisfying all my contstraints. The link https://docs.python.org/2/library/thread.html says:
thread.exit() Raise the SystemExit exception. When not caught, this will cause the thread to exit silently.
So I suppose there should be no problem of orphan processes, blocked resources, etc. Please suggest.
Post a Comment for "Terminating A Function Call In Python After N Seconds"