Python プログラミング

【Python】マルチスレッドの使い方【Thread】

Pythonには、マルチスレッドと呼ばれるテクニックがあります。
一般的にプログラム言語は上から下に順番に1行ずつ実行されるものです。
Pythonでは、それをスレッドとして複数同時に取り扱うことが可能です。
このテクニックをマルチスレッドと呼びます。
マルチスレッドを利用することで、全体の処理時間を短縮することができます。

今回は、そのマルチスレッドの使い方を解説します。

マルチスレッドとは?

通常、Pythonで実行している処理の流れは、上から順番に1行ずつ実行されます。
したがって、途中で何か重い処理があると、その行で処理が終了するまで、全体の処理の流れが止まってしまいます。

そこで、マルチスレッドが登場します。
1行ずつ実行される処理をスレッドとして、複数のスレッドを同時に実行することをマルチスレッドと呼びます。
これを活用することによって、重い処理はスレッドを分けて実行することで、全体の処理時間を短縮することができます。

Pythonでは、main関数から実行されるスレッドをマルチスレッドと呼びます。
mainスレッドから派生して作成・実行されたメソッドを単にスレッドと呼びます。

マルチスレッドは、全体の処理時間を短縮することができますが、デメリットもあります。
それは、処理の順番に整合性がなくなることです。
複数のスレッドで同じクラスや変数に対して処理を加える場合は、十分に注意する必要があります。

使い方

使い方はとても簡単です。
以下のソースコードを例として、解説します。

import threading
import time

def process():
    print('Process Start')
    time.sleep(3)
    print('Process End')

print('Main Start')

threadA = threading.Thread(target=process)
threadA.start()

time.sleep(1)

print('Main End')

1行目で、マルチスレッドに必要なモジュールをimportしてます。
threadingは、特にpip insallなどは不要で、標準で使用することができます。

2行目の、timeモジュールは、マルチスレッドとは特に関係しません。
6行目で、一定時間処理を止めるため、time.sleep()が使用したいので、importしました。

4~7行目はスレッドで実行する用の関数です。
time.sleep()を何らかの重たい処理と捉えてください。

11行目で、threadAと言うスレッドを定義しています。
threadAは、process()を実行するスレッドとなりました。
このように、スレッドの定義は、関数に対しておこないます。

12行目で、threadAを開始しました。
定義した通りに関数が実行されます。

さて、このソースコードの実行結果は以下の通りです。

Main Start
Process Start
Main End
Process End

11,12行目でスレッドを使ったので、process()の出力が後ろにきました。
また、実行時間もsleepは合計で4秒ありますが、3秒で終わりました。

11,12行目を、単にprocess()に置き換えた場合(つまり、マルチスレッド化しない場合)の実行結果は以下の通りです。
こちらの場合は、4秒かかりました。

Main Start
Process Start
Process End
Main End

追記:
上記の例では、threadAの終了を待たずにメインスレッドを終了させてしまっています。
実はこれは良くないことですので、下記補足のテクニックを使って、スレッドがmainスレッドよりも早く終わらないようにするか、デーモン化してください。

補足

join()を使ったスレッドの終了方法

上記の例では、メインのスレッドが先に終わってしまいました。
threadAとメインの終了を同時にしたい場合は、以下のようにjoinメソッドを使います。

import threading
import time

def process():
    print('Process Start')
    time.sleep(3)
    print('Process End')


print('Main Start')

threadA = threading.Thread(target=process)
threadA.start()

print('Main Process')
time.sleep(1)

threadA.join()

print('Main End')
Main Start
Main Process
Process Start
Process End
Main End

join()を使うことで、メインのスレッドは18行目で処理を停止して、threadAの終了を待ちます。

処理をどこかのタイミングで同期させたい時などに有効です。

デーモンを使ったスレッドの終了方法

上記の例では、メインスレッドがthreadAよりも早く終わってしまいました。
メインスレッド終了後も、threadAが生き残り、処理を続けることとなりました。

作ったスレッドをメインスレッドの終了とともに、終了させる方法があります。
それは、スレッドをデーモン化することです。

以下がスレッドのデーモン化の例です。

import threading
import time

def process():
    print('Process Start')
    time.sleep(3)
    print('Process End')


print('Main Start')

threadA = threading.Thread(target=process)
threadA.setDaemon(True)
threadA.start()

time.sleep(1)

print('Main End')
Main Start
Process Start
Main End

メインスレッドの終了を持って、threadAも終了したので、'Process End'は出力されませんでした。

非常に重たい処理があるときに、有効なテクニックです。

-Python, プログラミング
-,