2016-11-03 8 views
0

次のコードがあります。終了するには時間がかかりすぎると、ジョブを開始してプロセスを終了する必要があります。subprocess.check_output with timeoutプロセスの終了が延期されました

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

しかし、私場合に予想されていなかった、そのタスク:

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) 

コンソールは私に次のような出力を提供しますtasks.logを参照してください。

これは、t帽子のみ4秒のタスクが正常に終了しました。これは望ましく、期待されています。したがって、私のスクリプトの仕事の種類は、むしろ予期しない望ましくない方法です。

ブロックを解放する(プロセスを強制終了する)方法はありますか?つまり、MAX_WAITタイムアウトを超えたらすぐにですか?

ここで何が起こっているのですか、なぜsleepがスリープ状態になるまでPythonが待機するのですか?

EDIT - WORKING例

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) 

OUTPUT

$ 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コールで呼ばれるいくつかのプロセスが実際にあります。したがって、subprocesskillシグナルを送信すると、それはすべての子プロセスに送信されません。 check_outputは、他のプロセスが完了するのを待つことがあります。ソリューションについては、StackOverflowのpostをご覧ください。

関連する問題