Pythonでマルチスレッドの使い方や高速化について分かりやすく解説する

スポンサーリンク

Pythonのマルチスレッドは、スレッドの概念を用いてPythonでマルチタスク処理を実現する方法です。

スレッドとは何ですか?

スレッドとは、OSが管理するあらゆるプロセスの構成要素のことです。

OSはプロセスをスレッドに分割することで並列処理やマルチタスク処理を実現します。

スレッドは、実行の流れを分離して確保する軽量なプロセスです。

Pythonのマルチスレッドの利点は何ですか?

マルチスレッドアプリケーションを作成することには、非常に多くの利点があります。

ここでは、そのメリットのいくつかを見てみましょう。

  • リソースの有効活用
  • より多くの応答性
  • リソースを共有することで、より経済的になる
  • 並列処理によるマルチプロセッサ・アーキテクチャの効果的な使用
  • 時間の節約
  • スレッド(同一プロセスの一部)は、別プロセスである場合よりも容易に相互通信を行う。
  • メモリのオーバーヘッドをあまり必要としない
  • マルチスレッドサーバや対話型 GUI はマルチスレッドを排他的に使用します。
スポンサーリンク

Pythonでマルチスレッドを実現する方法とは?

では、最初のマルチスレッドアプリケーションの作成に移りましょう。

1. threading モジュールのインポート

スレッドを作成するために、threading モジュールを使用します。

import threading

threadingモジュールはThread` クラスからなり、このクラスはスレッドを作成するためにインスタンス化される。

スレッドは Thread クラスのオブジェクトを生成することで作成できる。

このクラスの引数は以下の通りです。

  1. target: スレッドによって呼び出される関数をここで指定します。この関数は、スレッドの run() メソッドによって呼び出される呼び出し可能なオブジェクトです。
  2. args: ここでは、target 関数の引数を指定します。
def print_hi(num):
    print("Hi, you are customer ",num)
 
t1 = threading.Thread(target=print_square, args=(10,))

上記のコードでは、 target パラメータとして呼び出された print_hi() 関数を呼び出しています。

この関数は num という 1 つのパラメータを持っており、args で指定します。

t1.start()

2. スレッドの起動

スレッドは、Thread オブジェクトに対して threading モジュールの start() メソッドを呼び出すことで開始されます。

以下はその説明図です。

def print_hi(num):
    print("Hi, you are customer ",num)
 
t1 = threading.Thread(target = print_hi, args=(10,))
t1.start()
t1.join()
print("End")

このメソッドはスレッドオブジェクトごとに最大一度だけ呼び出されなければなりません。

これは、オブジェクトの run() メソッドが制御の別スレッドで呼び出されるように手配します。

このメソッドは、同じスレッドオブジェクトに対して複数回呼び出されると RuntimeError を発生させます。

スレッドはそれ自体がプロセスであるプログラム内で呼び出されます。

そのため、スレッドの実行中は、メインプログラムも同様に実行を継続します。

したがって、作成されたスレッドが完了するまで、(メインスレッドによって実行される)メインプログラムの活動を一時停止させることができます。

その様子を図解すると、次のようになります。

3. スレッドの結合方法

Hi, you are customer 10
End

上のスニペットでは、Thread クラスを使ってオブジェクトを作成し、t1 という名前を付けています。

スレッドオブジェクト t1 に対して start() メソッドが呼び出され、スレッドアクティビティが開始されます。

次に、 join() メソッドが呼び出されます。

こうすることで、メインプログラムはメインスレッドの実行を停止し、スレッドt1が完了するまで待機するようにします。

t1がアクティビティを完了すると、メインスレッド(メインプログラム)は実行を継続することができます

したがって、print("End") という行は、スレッドの活動が完了してから実行される。

Illustration Of Main Thread And Child Threads
Illustration Of Main Thread And Child Threads

join()メソッドを使用しない場合、インタプリタは 2 つの print 文 -print(“Hi, you are customer “, num)print(“End”)` – のどちらかを選択する必要があります。

このようなシナリオでは、これらの行の実行はインタープリタによって選択されるため、どちらのprint文が先に表示されるかは予測することができません。

4. Pythonにおけるスレッドの同期

スレッドの同期とは、共有資源にアクセスする特定のプログラムセグメントを2つのスレッドが実行しないことを保証する仕組みと定義されます。

プログラムのそのようなセクションは、クリティカルセクションと呼ばれます。

2つのスレッドが同時にこのリソースにアクセスすることは、レースコンディションを引き起こす可能性があるため、確実に行う必要があります。

レースコンディションとは、2つ以上のスレッドが書き込み権限を持って共有リソースにアクセスし、データを変更しようとするシナリオのことを指します。

そのため、そのような変数の値は予測不可能になります。

そこで、ロックが解除されるまでプログラムの実行を一時的に停止させるロックをプログラム内で使用します。

これは、2つのスレッドが同じ変数にアクセスして競合が発生しないようにするためです。

マルチスレッドのデメリット

  • プログラムの複雑さが増す。
  • 共有リソース(オブジェクト、データ)の同期が必要。
  • 予測できない結果のデバッグが困難
  • スレッドの構築と同期にCPUやメモリを消費します。

参考文献

threading — Thread-based parallelism — Python 3.10.6 documentation
タイトルとURLをコピーしました