2016-11-03 61 views
0

我有一個下面的代碼,它應該開始一個作業,如果完成需要很長的時間就會終止進程。帶有超時進程終止的subprocess.check_output被推遲

import random 
from datetime import datetime 
from subprocess import check_output, STDOUT, TimeoutExpired 

MAX_WAIT = 5 

def custom_task(job=None): 
    sec = job or random.randint(4,10) 
    print('\nThis task will sleep {s} sec.'.format(s=sec)) 
    print('-/- {time} - - Commence new job <{job}>'.format(
      job=sec, time=datetime.now())) 
    try: 
     cmd = 'sleep {s}; echo "Woke up after {s} sec." | tee -a task.log'.format(s=sec) 
     stdout = check_output(cmd, shell=True, stderr=STDOUT, timeout=MAX_WAIT).decode() 
    except TimeoutExpired: 
     print('~/~ {time} - - Job <{job}> has been cancelled'.format(
       job=sec, time=datetime.now())) 
    except Exception as err: 
     print('!/! {time} - - Job <{job}> could not finish because of the error'.format(
       job=sec, time=datetime.now())) 
     print('{err}'.format(err=err)) 
    else: 
     print('=/= {time} - - Job <{job}> has been done'.format(
       job=sec, time=datetime.now())) 
     print(stdout) 

custom_task(4) 
custom_task(8) 

控制檯給我下面的輸出:

This task will sleep 4 sec. 
-/- 2016-11-03 01:07:56.037104 - - Commence new job <4> 
=/= 2016-11-03 01:08:00.051072 - - Job <4> has been done 
Woke up after 4 sec. 


This task will sleep 8 sec. 
-/- 2016-11-03 01:08:00.051233 - - Commence new job <8> 
~/~ 2016-11-03 01:08:08.062563 - - Job <8> has been cancelled 

注意這應該睡8秒8秒後釋放的塊,而不是預期的MAX_WAIT = 5

但是,如果我以後的任務檢查tasks.log我看到:

Woke up after 4 sec. 

這意味着t帽子只有4秒的任務已經成功完成,這是期望和期望的。因此,我的腳本類型的工作,但在相當意想不到的方式。

有沒有辦法按時釋放塊(終止進程),因此只要MAX_WAIT超時?

這裏發生了什麼,爲什麼python會等到sleep完成睡眠?

編輯 - 工作示例

from random import randint 
from os import killpg 
from signal import SIGKILL 
from datetime import datetime 
from subprocess import Popen, STDOUT, TimeoutExpired, PIPE 

MAX_WAIT = 5 

def custom_task(job=None): 
    sec = job or randint(4,10) 
    print('\n# This task will sleep {s} sec.'.format(s=sec)) 
    print('-/- {time} - - Commence new job <{job}>'.format(
      job=sec, time=datetime.now())) 
    cmd = 'sleep {s}; echo "Woke up after {s} sec." | tee -a tasks.log'.format(s=sec) 
    with Popen(cmd, shell=True, 
       stdout=PIPE, stderr=STDOUT, 
       close_fds=True, 
       universal_newlines=True, 
       start_new_session=True) as proc: 
     try: 
      stdout = proc.communicate(timeout=MAX_WAIT)[0] 
     except TimeoutExpired: 
      print('~/~ {time} - - Job <{job}> has been cancelled'.format(
        job=sec, time=datetime.now())) 
      killpg(proc.pid, SIGKILL) 
      stdout = proc.communicate(timeout=1)[0] 
     except Exception as err: 
      print('!/! {time} - - Job <{job}> could not finish because of the error'.format(
        job=sec, time=datetime.now())) 
      print('{err}'.format(err=err)) 
      killpg(proc.pid, SIGKILL) 
     else: 
      print('=/= {time} - - Job <{job}> has been done'.format(
        job=sec, time=datetime.now())) 
     print('# Return code: {}'.format(proc.returncode)) 
     print(stdout) 

custom_task(4) 
custom_task(30) 

輸出

$ time python3 popen.py 

# This task will sleep 4 sec. 
-/- 2016-11-05 15:38:13.833871 - - Commence new job <4> 
=/= 2016-11-05 15:38:17.842769 - - Job <4> has been done 
# Return code: 0 
Woke up after 4 sec. 


# This task will sleep 30 sec. 
-/- 2016-11-05 15:38:17.842942 - - Commence new job <30> 
~/~ 2016-11-05 15:38:22.849511 - - Job <30> has been cancelled 
# Return code: -9 


real 0m9.095s 
user 0m0.087s 
sys 0m0.000s 
+0

我常用的解決方法:用unix命令超時包裝命令咒語。然後,您可以捕獲返回碼狀態並按照手冊頁進行處理。 – Lmwangi

+0

感謝您的提示,我想我甚至比'python'實現更好,儘管它使我的腳本更加輕便,但在這種情況下,這不是問題。 –

回答

0

實際上有幾個過程堪稱check_output通話。所以當subprocess發送kill信號時,它不會被髮送到所有的後代進程。 check_output可能會等待其他進程完成。有關解決方案,請參閱StackOverflow上的此post