Python マルチプロセスの使い方

Python学習【365日チャレンジ!】218日目のマスターU(@Udemy11)です。

一日中椅子に座って映画を見ていたら、腰が痛くなってしまいました。

長時間立って釣りをしていたら腰が痛くなることはあったのですが、座っていても腰が痛くなるんですね。

ほんと年を取ると、体のそこかしこが悲鳴を上げてくるので困っちゃいます。

やっぱり、普段の食事や運動といった生活習慣をしっかりと見直したほうがいいような気がしますが、これまた決心して実行したとしても食事だけに誘惑に負けてしまうんですよね。

それでは、今日もPython学習をはじめましょう。

昨日の復習

昨日は、バリアについて学習しました。

バリアは、指定した数だけwait()が作られてから処理が実行されました。

例えばサーバーとクライアントが2つとも処理を受け入れられる状態になってから処理を行う操作などに使うことができました。

バリアの詳細については、昨日の記事をごらんください。

今日は、マルチプロセスについて学習します。

multiprocessing

multiprocessintモジュールには、スレッドで学習したLockRLock、Semapore、QueueEventConditionBarrierに加えて、ProcessValuePipeArrayManegerなどが用意されています。

スレッドで学習したものは、プロセスでも同じように使えますので、これからスレッドで学習していないクラスなどを学習していきたいとおもいます。

Processクラス

まずはProcessクラスを作ってスレッドと同じように扱ってみます。

import logging
import multiprocessing
import time

logging.basicConfig(
    level=logging.DEBUG, format='%(processName)s: %(message)s'
)

def worker1(i):
    logging.debug('start')
    time.sleep(i)
    logging.debug('end')

def worker2(i):
    logging.debug('start')
    time.sleep(i)
    logging.debug('end')

if __name__ == '__main__':
    i = 2
    t1 = multiprocessing.Process(target=worker1, args=(i, ))
    t2 = multiprocessing.Process(
            name='renamed worker', target=worker2, args=(i, ))
    t1.start()
    t2.start()

インポートするモジュールを、threadingからmultiprocessingに変更しています。

便宜上loggingtimeをインポートしていますが、実践時は必要に応じてライブラリをインポートします。

今回はプロセスなので、5行目からのlogging.basicConfigでは、threadNameではなくprocessNameに変更しています。

プロセスオブジェクトのターゲットとなるworker1worker2は、実行の際に代入する引数iをとって、time.sleep(i)i秒間、処理をポーズしています。

コードだけ見ると、threading.Threadmultiprocessing.Processに変わっただけですね。

実行結果は次のようになります。

Process-1: start
renamed worker: start #2秒待って次の行が表示される
Process-1: end
renamed worker: end

logging.basicConfigthreadNameからprocessNameに変更しているので、出力は、Process-1になっています。

daemon化

スレッドと同じようにプロセスもdaemon化することができます。

先程のコードの変更部分だけ抜き出してみます。

if __name__ == '__main__':
    i = 2
    t1 = multiprocessing.Process(target=worker1, args=(i, ))
    t1.daemon = True
    t2 = multiprocessing.Process(
            name='renamed worker', target=worker2, args=(i, ))
    t1.start()
    t2.start()
    t1.join()
    t2.join()

t1をデーモン化して、処理を最後まで実行するために27行目でjoin()しています。

28行目のt2.join()は、なくても問題はありませんが、明示的に記述するほうがいいということでしたね。

出力結果は同じですが、27行目と28行目を記述していない場合は、次のように、t2だけが実行された結果になります。

renamed worker: start
renamed worker: end

threadと同じ?

今日の学習では、threadと同じ処理になるので、何がちがうのかよくわかりませんでした。

最初、logging.basicConfigthreadNameprocessNameに変更していなかったので、普通にMainThread: startなんて出力されていたので、ちょっと戸惑ってしまいました。

ただ、どちらのプロセスもMainThreadだったので、1つのプロセスに1つのMainThreadが立ち上がってるんだろうな〜と理解することができました。

常にトライ&エラーを繰り返して、経験を積み上げることが大切ですね。

それでは明日も、Good Python!