【Python】 並列処理を理解しよう 【threadingの使い方 01】
はじめに
並列処理をしたいです。
複数のCPUコアまたはマルチスレッドを活用することで、一度に複数のタスクを同時に処理できます。これにより、タスクの合計実行時間が短縮され、アプリケーションのパフォーマンスが向上します。
今回はPythonで並列処理をする時の基本を紹介します。
【おさらい】 Pythonの処理の順番は?
読み飛ばしてもらっても構いません。
Pythonでは、処理をコードの上から順に実行していきます。
どのプログラミングでも基本は同じだと思います。
たとえば次のような感じ。
print("春はあけぼの。")
print("やうやう白くなりゆく山ぎは、")
print("すこしあかりて、")
print("紫だちたる雲のほそくたなびきたる。 ")
春はあけぼの。
やうやう白くなりゆく山ぎは、
すこしあかりて、
紫だちたる雲のほそくたなびきたる。
上から順に処理されています。
至って普通のコードです。
しかし、並列処理のコードを書くと、これが普通ではなくなります。
並列処理の基本
並列処理を書いていきます。
① threadingライブラリを使います
Pythonで並列処理をしたい時、「 threading 」というライブラリを使用します。
標準ライブラリなので、pip install XX
はしなくてOKです。
次のように使用します。
import threading
print(threading.currentThread().getName())
MainThread
上記のコードは、「 いま動作しているスレッドを確認する 」というコードです。
ツラツラと書いていますが、コピペでOKです。
結果から、「 MainThread 」というスレッドにいることがわかりました。
イメージとしては、下図のようなメインとなる線の上にいる感じです。
さて、もう少し詳しくみていきましょう。
② スレッドを増やしてみよう
import threading
import time
# うどんを茹でる関数
def boil_udon():
print(" ◆スレッド:", threading.currentThread().getName())
print(' うどんを茹でます。')
time.sleep(3)
print(' うどんが茹であがりました。')
# メイン
if __name__ == "__main__":
print("◆スレッド:", threading.currentThread().getName())
print('うどんを作ります。')
# スレッドを作る
thread1 = threading.Thread(target=boil_udon)
# スレッドの処理を開始
thread1.start()
# スレッドの処理を待つ
thread1.join()
print('盛り付けます。')
print('うどんができました。')
※こちらのコードを参考にしました。わかりやすいです。↓
たくさんコードを追加しましたが、コピペでOKです。
実行すると次のような結果が返ってくるはずです。
◆スレッド: MainThread
うどんを作ります。
◆スレッド: Thread-1
うどんを茹でます。
うどんが茹であがりました。
盛り付けます。
うどんができました。
メインのスレッドの処理がされてから、別のスレッド(Thread-1)の処理が実行されています。
このコードが何をしているか、簡単に解説します。
まず、「 threading.Thread() 」でスレッドを追加することができます。名前を指定しなければ、連番で「 Thread-1 」のようにが自動で命名されます。引数のtarget
に並列処理したい関数を渡します。
そして、.start()
を実行することで、スレッドが開始して、boil_udon
内の処理が進んでいきます。この時、MainThread
と並行して処理されていることに注意してください。
.join()
を実行することで、Thread-1
の処理が終わるまで、MainThread
が待ってくれます。Thread-1
の処理が終わると、次の処理へと進みます。
イメージにすると、下図のような感じです。
③ スレッドをもう1つ追加してみる
上記のコードで、うどんを茹でることができました。
このコードに、うどんのツユを作る関数を追加して、うどんを茹でている間にツユを作りましょう。
うどんを茹でるのと、ツユを作るのを同時に行う、つまり並列処理です。
import threading
import time
# うどんを茹でる関数
def boil_udon():
print(" ◆スレッド:", threading.currentThread().getName())
print(' うどんを茹でます。')
time.sleep(3)
print(' うどんが茹であがりました。')
# ツユを作る関数
def make_tuyu():
print(" ◆スレッド:", threading.currentThread().getName())
print(' ツユをつくります。')
time.sleep(2)
print(' ツユができました。')
# メイン
if __name__ == "__main__":
print("◆スレッド:", threading.currentThread().getName())
print('うどんを作ります。')
# スレッドを作る
thread1 = threading.Thread(target=boil_udon)
thread2 = threading.Thread(target=make_tuyu)
# スレッドの処理を開始
thread1.start()
thread2.start()
# スレッドの処理を待つ
thread1.join()
thread2.join()
print('盛り付けます。')
print('うどんができました。')
結果は次のようになります。
◆スレッド: MainThread
うどんを作ります。
◆スレッド: Thread-1
うどんを茹でます。
◆スレッド: Thread-2
ツユをつくります。
ツユができました。
うどんが茹であがりました。
盛り付けます。
うどんができました。
Thread-2
の処理が追加され、並列に動作していることがわかります。
これで、うどんを茹でている間にツユを作ることができました。
図にすると下図のようなイメージです。
まとめ
-
threading.Thread
でスレッドを追加できる - 引数にスレッドに実行させたい関数を渡す
-
.start()
メソッドで、スレッドが開始される -
.join()
メソッドで、スレッドが終わるまで待つ
参考
Discussion