現在、subprocess.Popen(cmd, Shell=TRUE)
を使用してプログラムを起動しています
私はPythonにかなり慣れていませんが、次のようなことを行うことができるapiがあるはずだと「感じ」ます。
_subprocess.Popen(cmd, Shell=TRUE, postexec_fn=function_to_call_on_exit)
_
これは、コマンドが終了したことを知ることに基づいて_function_to_call_on_exit
_が何かを実行できるようにするためです(たとえば、現在実行中の外部プロセスの数のカウントを維持します)。
スレッド化とPopen.wait()
メソッドを組み合わせたクラスでサブプロセスをかなり簡単にラップできると思いますが、Python)でスレッド化をまだ実行していないため、これは、APIが存在するのに十分一般的かもしれないので、最初に見つけようと思った。
前もって感謝します :)
その通りです-これには素晴らしいAPIはありません。 2番目の点も正しいです。スレッドを使用してこれを行う関数を設計するのは簡単です。
import threading
import subprocess
def popenAndCall(onExit, popenArgs):
"""
Runs the given args in a subprocess.Popen, and then calls the function
onExit when the subprocess completes.
onExit is a callable object, and popenArgs is a list/Tuple of args that
would give to subprocess.Popen.
"""
def runInThread(onExit, popenArgs):
proc = subprocess.Popen(*popenArgs)
proc.wait()
onExit()
return
thread = threading.Thread(target=runInThread, args=(onExit, popenArgs))
thread.start()
# returns immediately after the thread starts
return thread
Pythonでもスレッド化はかなり簡単ですが、onExit()が計算コストが高い場合は、マルチプロセッシングを使用する代わりにこれを別のプロセスに配置することをお勧めします(GILがプログラムの速度を低下させないようにするため)。実際には非常にシンプルです。threading.Thread
へのすべての呼び出しをmultiprocessing.Process
に置き換えることができます。これは、それらが(ほぼ)同じAPIに従っているためです。
= concurrent.futures
モジュールがPython 3.2にあります(古いバージョンではpip install futures
で利用可能Python <3.2):
pool = Pool(max_workers=1)
f = pool.submit(subprocess.call, "sleep 2; echo done", Shell=True)
f.add_done_callback(callback)
コールバックは、f.add_done_callback()
を呼び出したのと同じプロセスで呼び出されます。
import logging
import subprocess
# to install run `pip install futures` on Python <3.2
from concurrent.futures import ThreadPoolExecutor as Pool
info = logging.getLogger(__name__).info
def callback(future):
if future.exception() is not None:
info("got exception: %s" % future.exception())
else:
info("process returned %d" % future.result())
def main():
logging.basicConfig(
level=logging.INFO,
format=("%(relativeCreated)04d %(process)05d %(threadName)-10s "
"%(levelname)-5s %(msg)s"))
# wait for the process completion asynchronously
info("begin waiting")
pool = Pool(max_workers=1)
f = pool.submit(subprocess.call, "sleep 2; echo done", Shell=True)
f.add_done_callback(callback)
pool.shutdown(wait=False) # no .submit() calls after that point
info("continue waiting asynchronously")
if __name__=="__main__":
main()
$ python . && python3 .
0013 05382 MainThread INFO begin waiting
0021 05382 MainThread INFO continue waiting asynchronously
done
2025 05382 Thread-1 INFO process returned 0
0007 05402 MainThread INFO begin waiting
0014 05402 MainThread INFO continue waiting asynchronously
done
2018 05402 Thread-1 INFO process returned 0
Daniel Gの回答を変更して、subprocess.Popenでキーワード引数を使用したかったので、個別のタプル/リストとしてではなく、subprocess.Popen argsとkwargsをそのまま渡すようにしました。
私の場合、postExec()
の後に実行したいメソッドsubprocess.Popen('exe', cwd=WORKING_DIR)
がありました
以下のコードでは、単にpopenAndCall(postExec, 'exe', cwd=WORKING_DIR)
になります。
import threading
import subprocess
def popenAndCall(onExit, *popenArgs, **popenKWArgs):
"""
Runs a subprocess.Popen, and then calls the function onExit when the
subprocess completes.
Use it exactly the way you'd normally use subprocess.Popen, except include a
callable to execute as the first argument. onExit is a callable object, and
*popenArgs and **popenKWArgs are simply passed up to subprocess.Popen.
"""
def runInThread(onExit, popenArgs, popenKWArgs):
proc = subprocess.Popen(*popenArgs, **popenKWArgs)
proc.wait()
onExit()
return
thread = threading.Thread(target=runInThread,
args=(onExit, popenArgs, popenKWArgs))
thread.start()
return thread # returns immediately after the thread starts
私は同じ問題を抱えており、multiprocessing.Pool
。関連する2つのハックトリックがあります。
結果は、完了時にコールバックで実行される1つの関数です
def sub(arg):
print arg #prints [1,2,3,4,5]
return "hello"
def cb(arg):
print arg # prints "hello"
pool = multiprocessing.Pool(1)
rval = pool.map_async(sub,([[1,2,3,4,5]]),callback =cb)
(do stuff)
pool.close()
私の場合、呼び出しもノンブロッキングにする必要がありました。美しく働く
私はDaniel G.の回答に触発され、非常に単純なユースケースを実装しました。私の仕事では、異なる引数を使用して同じ(外部)プロセスを繰り返し呼び出す必要があることがよくあります。特定の各呼び出しがいつ行われたかを判別する方法をハッキングしていましたが、コールバックを発行するためのはるかに明確な方法があります。
この実装は非常にシンプルなので気に入っています。複数のプロセッサに非同期呼び出しを発行し(multiprocessing
ではなくthreading
を使用することに注意)、完了時に通知を受け取ることができます。
私はサンプルプログラムをテストし、うまくいきました。自由に編集してフィードバックを提供してください。
import multiprocessing
import subprocess
class Process(object):
"""This class spawns a subprocess asynchronously and calls a
`callback` upon completion; it is not meant to be instantiated
directly (derived classes are called instead)"""
def __call__(self, *args):
# store the arguments for later retrieval
self.args = args
# define the target function to be called by
# `multiprocessing.Process`
def target():
cmd = [self.command] + [str(arg) for arg in self.args]
process = subprocess.Popen(cmd)
# the `multiprocessing.Process` process will wait until
# the call to the `subprocess.Popen` object is completed
process.wait()
# upon completion, call `callback`
return self.callback()
mp_process = multiprocessing.Process(target=target)
# this call issues the call to `target`, but returns immediately
mp_process.start()
return mp_process
if __== "__main__":
def squeal(who):
"""this serves as the callback function; its argument is the
instance of a subclass of Process making the call"""
print "finished %s calling %s with arguments %s" % (
who.__class__.__name__, who.command, who.args)
class Sleeper(Process):
"""Sample implementation of an asynchronous process - define
the command name (available in the system path) and a callback
function (previously defined)"""
command = "./sleeper"
callback = squeal
# create an instance to Sleeper - this is the Process object that
# can be called repeatedly in an asynchronous manner
sleeper_run = Sleeper()
# spawn three sleeper runs with different arguments
sleeper_run(5)
sleeper_run(2)
sleeper_run(1)
# the user should see the following message immediately (even
# though the Sleeper calls are not done yet)
print "program continued"
出力例:
program continued
finished Sleeper calling ./sleeper with arguments (1,)
finished Sleeper calling ./sleeper with arguments (2,)
finished Sleeper calling ./sleeper with arguments (5,)
以下はsleeper.c
のソースコードです-私のサンプル「時間のかかる」外部プロセス
#include<stdlib.h>
#include<unistd.h>
int main(int argc, char *argv[]){
unsigned int t = atoi(argv[1]);
sleep(t);
return EXIT_SUCCESS;
}
次のようにコンパイルします。
gcc -o sleeper sleeper.c
私の知る限り、少なくともsubprocess
モジュールにはそのようなAPIはありません。おそらくスレッドを使用して、自分で何かを転がす必要があります。