Pythonで複数プロセスたちあげて結果を非同期で処理する

ていうのを見かけたので殺伐とした心を癒すためにやってみる心。

やりたい事

  • Pythonスクリプトから複数外部コマンドを実行
  • 実行結果を非同期に処理する
  • 割とお手軽にやりたい。

試してみる

それぞれ 1秒, 2秒, 3秒まってprintするようなスクリプトを3つ用意

# こんな感じ
from time import sleep

sleep(1)
print "echo1"

これを参考のまんま下記のようなコードで動かす。

from subprocess import Popen, PIPE
import time

running_procs = [
    Popen(['python', 'echo3.py'], stdout=PIPE, stderr=PIPE),
    Popen(['python', 'echo2.py'], stdout=PIPE, stderr=PIPE),
    Popen(['python', 'echo1.py'], stdout=PIPE, stderr=PIPE),
    ]

while running_procs:
    for proc in running_procs:
        retcode = proc.poll()
        if retcode is not None: # Process finished.
            running_procs.remove(proc)
            break
    else: # No process is done, wait a bit and check again.
        time.sleep(.1)
        continue

    # Here, `proc` has finished with return code `retcode`
    if retcode != 0:
        """Error handling."""
        print "error"

    print proc.stdout.read()

結果はこうなる

$ python hoge.py
echo1

echo2

echo3

やってること

1. 3つのプロセスをリストにしてfor inでまわす
2. proc.pollでreturn codeをチェック
3. return codeが帰ってきたら自らをプロセスリストから消してfor終了
4. 逆に3つのプロセスどれもがreturn codeかえさなかったら、ほんのちょっぴりまって1にもどる
5. return codeが帰ってきたらerror handling or stdoutから結果を受け取る

お手軽にやるにはいいかもしんない. proc.poll こういう挙動になるんだてのを知った。

備考

  • コマンドが終わるのpollingして、結果が帰ってきた順番に処理してるだけなので正確には非同期で処理してるとは言えない。
  • 例えば最後のstdoutからreadしたものをあれやらこれや処理するとしたら、その間ブロックされるので、その後の結果は処理されるのが待たされる。

はー癒された