66797


Pythonについて。

只今、『入門 Python3』を読みながら、  
JupyterLabを使ってPythonを学んでいます。  
OSはwindows10です。

『11.1.2 プロセス』より。  
以下のコードは、皿洗いプロセスと乾燥プロセスで仕事を分担し、`JoinableQueue`を使って、その間で通信をしながらタスクを処理するというプログラムです。  
1つの皿洗いプロセスと複数の乾燥担当プロセスがあり、間に`dish_queue`があります。

> 本に書かれてある通りに書いてもエラーが発生して実行できないため、`if __name__ == '__main__' :`の行を追加しています。
[code]

    import multiprocessing as mp
    
    # 皿洗い担当の定義をする。
    
    def washer(dishes, output) :
        for dish in dishes :
            print('Washing', dish, 'dish')
            output.put(dish)
    
    # 皿の乾燥担当の定義をする。
    
    def dryer(input) :
        while True :
            dish = input.get()
            print('Drying', dish, 'dish')
            input.task_done()
    
    # mp.JoinableQueueオブジェクトを定義し、乾燥担当をデーモンプロセスにして待機させておく。
    
    dish_queue = mp.JoinableQueue()
    if __name__ == '__main__' :
        dryer_process = mp.Process(target = dryer, args = (dish_queue,))
        dryer_process.daemon = True
        dryer_process.start()
    
    # 皿のリストを定義し、皿洗い担当を実行する。
    # 最終行で、mp.JoinableQueueオブジェクトのjoin()メソッドを使い、洗った皿(タスクキュー)を皿洗い担当から乾燥担当に渡す。
    
    dishes = ['salad', 'bread', 'entree', 'dessert']
    washer(dishes, dish_queue)
    dish_queue.join()
    
[/code]

これを実行すると、本来はこのようになります。

[code]

    Washing salad dish
    Washing bread dish
    Washing entree dish
    Washing dessert dish
    Drying salad dish
    Drying bread dish
    Drying entree dish
    Drying dessert dish
    
[/code]

しかし、実際には、

[code]

    Washing salad dish
    Washing bread dish
    Washing entree dish
    Washing dessert dish
    Washing salad dish
    Washing bread dish
    Washing entree dish
    Washing dessert dish
    
[/code]

と表示されました。  
なんということでしょうか、皿を二度も洗っています。

ここで質問です。  
1.このコードは、どうすれば想定通りの結果になりますか？  
2.この一連の流れは、同期的と言えますか？それとも非同期的でしょうか？

尚、言葉や言葉の使い方が正確ではないことがあります。  
本文中、気になることがございましたら、何なりとご指摘くださいませ。  
それは、自身の成長のために必要なことだと思っております。


CC BY-SA 4.0
2020-05-19T19:13:11.910
66793
2020-05-20T01:14:09.337
2020-05-19T22:56:57.953
19110
39908
question
python python-multiprocessing
multiprocessingモジュールのJoinableQueueを使った複数のプロセスの実行について


**Q1.このコードは、どうすれば想定通りの結果になりますか？**  
A1.最後の3行をインデントさせて、`if __name__ == '__main__'
:`の判断の中で実行させれば、希望の動作になります。(実は何故かは分かりませんが)

[code]

    if __name__ == '__main__' :
        dryer_process = mp.Process(target = dryer, args = (dish_queue,))
        dryer_process.daemon = True
        dryer_process.start()
    
    # 皿のリストを定義し、皿洗い担当を実行する。
    # 最終行で、mp.JoinableQueueオブジェクトのjoin()メソッドを使い、洗った皿(タスクキュー)を皿洗い担当から乾燥担当に渡す。
    
        dishes = ['salad', 'bread', 'entree', 'dessert']
        washer(dishes, dish_queue)
        dish_queue.join()
    
[/code]

**Q2.この一連の流れは、同期的と言えますか？それとも非同期的でしょうか？**  
A2.同期的と言う方が強いですね。それよりも順序的(今思いついた説明用の言葉ですが)という感じですが。`Washing`と`Dryng`が混じらずに連続して処理されるので。

また例えば`import time`して`def washer(dishes, output)
:`の`output.put(dish)`の後に`time.sleep(0.1)`とでも入れると、`Washing`と`Dryng`が交互に表示されて非同期性が強くなるように見えますが、それでも`washer`が何かを処理した後に`dryer`がそれを待って同期して処理するのは変わりません。

それらが前後したり(前後しても動作したり)、もっとバラバラに`'salad', 'bread', 'entree',
'dessert'`の処理順番が入れ替わったりして動作するならば非同期的と言えるでしょう。


CC BY-SA 4.0
2020-05-19T23:51:21.300
66796
2020-05-20T00:06:34.070
2020-05-20T00:06:34.070
26370
26370
66793
answer


グローバルスコープで関数呼び出しなどしている部分を、全て `if __name__ == '__main__'` 下に入れてください。

[code]

    if __name__ == '__main__' :
        dish_queue = mp.JoinableQueue()
    
        dryer_process = mp.Process(target = dryer, args = (dish_queue,))
        dryer_process.daemon = True
        dryer_process.start()
    
        dishes = ['salad', 'bread', 'entree', 'dessert']
        washer(dishes, dish_queue)
        dish_queue.join()
    
[/code]

multiprocessing ではマルチプロセスを実現するための手法として fork、spawn、forkserver が選べますが、fork
以外で実現している場合、新しくプロセスを開始する際にそのプロセスにおいて自分自身が import されます。このとき `if __name__ ==
'__main__'` されていない部分のプログラムが実行されてしまうため、意図しない動作が起きてしまいます。詳しくは multiprocessing
の[プログラミングガイドライン「メインモジュールの安全なインポート」](https://docs.python.org/ja/3/library/multiprocessing.html#the-
spawn-and-forkserver-start-methods)をご覧ください。

### 同期・非同期について

まず今回の場合は、メインプロセスとデーモンプロセスの間のキューを介した通信が同期的か非同期的かを考えることができます。プログラム全体の流れが同期的だとか非同期的だとか言うのはやや不自然です。なぜならあるプログラムの中で同期的な通信と非同期的な通信を混ぜて使えるからです。

今回のプログラムでは `dryer` というデーモンプロセスとメインプロセスがキュー `dish_queue`
をバッファーとして使いながら通信しています。プロセスとキューの間の送受信は、キューへの put もキューからの get
もそれぞれが終わらないと次の動作に進めない形になっており、この意味ではどちらも同期的な通信をしています。ただ、キューを介したプロセス間通信という意味で見ると、同期・非同期という言い方ではなく、キューというバッファーを使ったノンブロッキングな送信を行っている、と言った方が分かりやすいかもしれません。


CC BY-SA 4.0
2020-05-20T01:14:09.337
66797
2020-05-20T01:14:09.337
19110
66793
answer