ryryrymyg / kaggle_for_me

This is repository that help me to understand kaggle competition
0 stars 0 forks source link

📝 Pythonのthreadingとmultiprocessingを完全理解 #4

Open ryryrymyg opened 3 years ago

ryryrymyg commented 3 years ago

以下ページについてまとめる https://qiita.com/kaitolucifer/items/e4ace07bd8e112388c75

ryryrymyg commented 3 years ago

Pythonでマルチタスクを同時に処理するための方法

ryryrymyg commented 3 years ago

multiprocessing

 Unix系OSではfork()というシステムコールで、プロセスを作成できます。fork()を呼び出すと、現在のプロセスをコピーします。コピーされたプロセスを子プロセスと言い、元のプロセスはその親プロセスになります。fork()の戻り値は、子プロセスと親プロセス両方に返します。そして、子プロセスの戻り値は0で、親プロセスの中では子プロセスのIDが返されます。その理由は、親プロセスは子プロセスのIDを記録しなければならないからです。子プロセスからgetpidで親プロセスのIDを取得できます。

pythonのosモジュールではシステムコール系がカプセル化されている。

import os

print('Process ({}) start...'.format(os.getpid()))
# Only works on Unix/Linux/Mac:
pid = os.fork()
if pid == 0:
    print('I am child process ({}) and my parent is {}.'.format(os.getpid(), os.getppid()))
else:
    print('I ({}) just created a child process ({}).'.format(os.getpid(), pid))

実行結果:

Process (19148) start...
I (19148) just created a child process (19149).
I am child process (19149) and my parent is 19148.
ryryrymyg commented 3 years ago

Pythonのマルチプロセスのプログラムを作成する時は、標準ライブラリのmultiprocessingモジュールを使うのをお勧めします。multiprocessingモジュールは並列処理可能なモジュールです。threadingモジュールはGILのせいで並列処理ができないため、multiprocessingモジュールが実装されたとも言われています。

また、multiprocessingモジュールはクロスプラットフォームで、Windowsでもマルチプロセスのプログラムを作成できます。前述のように、Windowsはfork()を持ってないため、multiprocessingモジュールでプロセスを作る時は、擬似fork()の処理をしています。やり方として、親プロセスの全てのPythonオブジェクトをPickleでシリアライズして、子プロセスに渡すようにしています。なので、Windowsでmultiprocessingモジュールの呼び出しが失敗したら、Pickleのほうで失敗している可能性があります。

ryryrymyg commented 3 years ago

プロセス(Process)

プロセスを使って簡単に子プロセスを作成できる

from multiprocessing import Process
import os

# 子プロセスが実行する処理
def run_proc(name):
    print('Run child process {} ({})...'.format(name, os.getpid()))

print('Parent process {}.'.format(os.getpid()))
p = Process(target=run_proc, args=('test',))
print('Child process will start.')
p.start()
p.join()
print('Child process end.')

実行結果:

Parent process 19218.
Child process will start.
Run child process test (19219)...
Child process end.
ryryrymyg commented 3 years ago

joinメソッドは強制的にプロセスを実行させるためのメソッド

ryryrymyg commented 3 years ago

プロセスプール(Process Pool)

子プロセスの作成には非常に計算コストがかかる -> 大量に作りたいときはPoolでプロセスプールを作成する。

Poolの主なメソッド

メソッド 説明
apply 同期処理。同期処理の場合、親プロセスは子プロセスの終了を待つ
apply_async 非同期処理。非同期処理の場合親プロセスは子プロセスの終了を待たない
terminate 直ちに終了する
join 親プロセスは子プロセスの処理が終わるまで待機する。プロセスのjoinはcloseかterminateの後でしか実行できない
close すべてのプロセスの処理が終わったら終了する。
from multiprocessing import Pool
import os
import time
import random

def long_time_task(name):
    print('Run task {} ({})...'.format(name, os.getpid()))
    start = time.time()
    time.sleep(random.random() * 3)
    end = time.time()
    print('Task {} runs {} seconds.'.format(name, (end - start)))

print('Parent process {}.'.format(os.getpid()))
p = Pool(4)  # 同時に最大4個の子プロセス
for i in range(5):
    p.apply_async(long_time_task, args=(i,))
# 非同期処理のため、親プロセスは子プロセスの処理を待たずに、
# 次のprintをする
print('Waiting for all subprocesses done...')
p.close()
p.join()
print('All subprocesses done.')

実行結果

Parent process 19348.
Waiting for all subprocesses done...
Run task 0 (19349)...
Run task 1 (19350)...
Run task 2 (19351)...
Run task 3 (19352)...
Task 1 runs 0.8950300216674805 seconds.
Run task 4 (19350)...
Task 2 runs 1.0132842063903809 seconds.
Task 4 runs 0.3936619758605957 seconds.
Task 3 runs 2.3689510822296143 seconds.
Task 0 runs 2.776203155517578 seconds.
All subprocesses done.

プールサイズは4なので、task 4はtask 0からtask 3のどれかが終了してから実行し始めます。

ryryrymyg commented 3 years ago

とりあえずPoolがわかればよいのでここまで。 追記が必要な場合は再度ISSUEを開く 残りの題目は