Pythonのマルチプロセスプール

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

ふと思ったのですが、レジ袋の有料化よりも、ペットボトルのデジポット制度を導入したほうが環境に優しいんじゃないかと思うんです。

子供の頃は、ファンタの1Lとかも瓶で、駄菓子屋に持っていって瓶代金をもらうなんてことをしてお小遣いにしていたものです。

瓶の場合は、洗って再利用されているので、ペットボトルとは少し違いますが、ペットボトルをお金に変えることができればペットボトルを捨てる人なんていなくなるんじゃないかと思いませんか?

ヨーロッパの方では、すでに導入されている地域があるようですが、システムの構築などにコストや時間がかかるんでしょうね。

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

昨日の復習

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

マルチプロセスは、マルチスレッドと同じような使い方なので、threadprocessに変更するだけで使うことができました。

スレッドで学習したLockRLockSemaporeQueueEventConditionBarrierなどがthreadと同じように使えます。

daemonjoin()を使ったコードも学習しました。

マルチプロセスの詳細については、昨日の記事をごらんください。

今日は、プールを使って非同期処理をする方法について学習します。

非同期処理とは

まず最初に、「非同期処理とはなんぞや?」ってところでつまづきました。

なので、非同期処理について少し調べてみました。

非同期処理とは

ある関数が呼び出されたとき、戻り値として本来渡したい結果を返すのではなく、一度関数としては終了し(=呼び出し元に戻る)、後で『本来渡したかった値』を返せる状態になったときに、呼び出し元にその値を通知する仕組み

とりあえず、ボールを投げておいてそのボールが相手から返ってくるまでに、他の人にちがうボールを渡して、最初のボールが返ってきたらそのボールをバットで打つという感じでしょうか。

いつもながら、よくわからない例えになってしまってますが、最終的な処理の終了を待たずに、メインの処理で次の処理ができるということです。

これまで最初のコードの処理が終わってから次のコードの処理をしたり、最初のコードの処理を始めたあと、次のコードを処理するけど、最初のコードの処理の完了を待たずに終了したりする並列処理を学習してきましたが、今回の非同期処理は、最初のコードの処理をはじめてから次のコードの処理も実行して、最初のコードの結果が返ってくるのを待ってなにかの処理をするという感じです。

デーモン化したあとのjoin()みたいな感じですが、いまいちどうちがうのか理解できていません。

とりあえず、コードを書いていきましょう。

Pool

multiprocess.Poolというメソッドをつかって非同期処理を実行します。

import logging
import multiprocessing
import time

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

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

if __name__ == '__main__':
    with multiprocessing.Pool(1) as p:
        p1 = p.apply_async(worker1, (100, ))
        logging.debug('executed')
        logging.debug(p1.get())

12行目までは昨日と同じです。

13行目で関数worker1の返り値をiにするコードを加えています。

16行目でwithステートメントを使ってmultiprocessing.Poolを開いていますが、引数は1にしています。

17行目では、非同期処理のターゲット関数をworker1にして引数に100を入れた非同期処理オブジェクトp1を作成しています。

18行目では、executedを出力していますが、17行目のp1オブジェクトを作成した際に実行された関数の返り値がメインプロセスに返ってくる前に、executedが出力されます。

最初の例えで言うところのボールを投げたあとに他の人にボールを渡すというところですね。(よけいわかりづらいわ!ってツッコミはなしで。。。)

最後の行で、関数worker1からの返り値i(100)get()で取得して出力しています。

実行結果

MainProcess: executed
ForkPoolWorker-1: start #次の行の出力まで5秒まつ
ForkPoolWorker-1: end
MainProcess: 100

2行目までが出力されたあと、5秒経ってから残りの行が出力されます。

ちょっとした違い

昨日学習したマルチプロセスもマルチスレッドの処理と同じような感じでしたが、今回書いたコードと同じ結果を得るには、スレッドを使ってもできますよね。

例題的に書いているコードなのでシンプルになっていますが、複雑な処理をするようになれば、スレッドと違ったプロセスの使い方がわかってくるんだと思います。

まずは基本的な使い方をマスターしましょう。

それでは明日もGood Python!