Python スレッディング: はじめに
公開: 2022-10-25このチュートリアルでは、Python の組み込みスレッドモジュールを使用して、Python のマルチスレッド機能を調べる方法を学習します。
プロセスとスレッドの基本から始めて、並行性と並列性の概念を理解しながら、Python でマルチスレッドがどのように機能するかを学びます。 次に、組み込みのthreading
モジュールを使用して、Python で 1 つ以上のスレッドを開始および実行する方法を学習します。
始めましょう。
プロセスとスレッド: 違いは何ですか?
プロセスとは
プロセスは、実行する必要があるプログラムのインスタンスです。
Python スクリプトや Chrome などの Web ブラウザからビデオ会議アプリケーションまで、何でもかまいません。 マシンでタスク マネージャーを起動し、[パフォーマンス] –> [ CPU ] に移動すると、現在 CPU コアで実行されているプロセスとスレッドを確認できます。

プロセスとスレッドについて
内部的に、プロセスには、プロセスに対応するコードとデータを格納する専用メモリがあります。
プロセスは 1 つ以上のスレッドで構成されます。 スレッドは、オペレーティング システムが実行できる最小の命令シーケンスであり、実行の流れを表します。
各スレッドには独自のスタックとレジスタがありますが、専用メモリはありません。 プロセスに関連付けられたすべてのスレッドがデータにアクセスできます。 したがって、データとメモリはプロセスのすべてのスレッドで共有されます。

N 個のコアを持つ CPU では、N 個のプロセスを同時に同時に実行できます。 ただし、同じプロセスの 2 つのスレッドを同時に実行することはできませんが、同時に実行することはできます。 次のセクションでは、同時実行と並列処理の概念について説明します。
これまでに学んだことに基づいて、プロセスとスレッドの違いを要約しましょう。
特徴 | プロセス | スレッド |
メモリー | 専用メモリ | 共有メモリ |
実行モード | 並列、同時 | 同時; しかし平行ではない |
によって処理される実行 | オペレーティング·システム | CPython インタープリター |
Python でのマルチスレッド
Python では、グローバル インタープリター ロック (GIL) により、任意の時点で1 つのスレッドのみがロックを取得して実行できることが保証されます。 実行するには、すべてのスレッドがこのロックを取得する必要があります。 これにより、特定の時点で 1 つのスレッドのみが実行されるようになり、同時マルチスレッド化が回避されます。
たとえば、同じプロセスの 2 つのスレッドt1
とt2
について考えてみます。 スレッドは、 t1
が特定の値k
を読み取っているときに同じデータを共有するため、 t2
が同じ値k
を変更する可能性があります。 これにより、デッドロックや望ましくない結果が生じる可能性があります。 ただし、いずれかのインスタンスでロックを取得して実行できるスレッドは 1 つだけです。 したがって、GIL はスレッド セーフも保証します。
では、Python でマルチスレッド機能を実現するにはどうすればよいでしょうか。 これを理解するために、並行性と並列性の概念について説明しましょう。
並行性と並列性: 概要
複数のコアを持つ CPU を考えてみましょう。 下の図では、CPU に 4 つのコアがあります。 これは、任意の時点で 4 つの異なる操作を並行して実行できることを意味します。
4 つのプロセスがある場合、各プロセスは 4 つのコアのそれぞれで独立して同時に実行できます。 各プロセスに 2 つのスレッドがあるとします。

スレッド化の仕組みを理解するために、マルチコア プロセッサ アーキテクチャからシングルコア プロセッサ アーキテクチャに切り替えてみましょう。 前述のように、特定の実行インスタンスでアクティブにできるスレッドは 1 つだけです。 ただし、プロセッサ コアはスレッド間で切り替えることができます。

たとえば、I/O バウンド スレッドは I/O 操作 (ユーザー入力の読み取り、データベースの読み取り、ファイル操作) を待機することがよくあります。 この待機時間中に、他のスレッドが実行できるようにロックを解放できます。 待機時間は、 n
秒間スリープするなどの簡単な操作でもかまいません。
要約すると、待機操作中に、スレッドはロックを解放し、プロセッサ コアが別のスレッドに切り替えられるようにします。 待機期間が完了すると、前のスレッドが実行を再開します。 プロセッサ コアがスレッド間を同時に切り替えるこのプロセスは、マルチスレッド化を容易にします。
アプリケーションにプロセス レベルの並列処理を実装する場合は、代わりにマルチプロセッシングの使用を検討してください。
Python スレッド化モジュール: 最初のステップ
Python には、Python スクリプトにインポートできるthreading
モジュールが付属しています。
import threading
Python でスレッド オブジェクトを作成するには、 Thread
コンストラクターthreading.Thread(...)
を使用できます。 これは、ほとんどのスレッド化の実装で十分な一般的な構文です。
threading.Thread(target=...,args=...)
ここ、
-
target
は Python callable を示すキーワード引数です args
は、ターゲットが受け取る引数のタプルです。
このチュートリアルのコード例を実行するには、Python 3.x が必要です。 コードをダウンロードして、手順に従ってください。
Python でスレッドを定義して実行する方法
ターゲット関数を実行するスレッドを定義しましょう。
対象関数はsome_func
です。
import threading import time def some_func(): print("Running some_func...") time.sleep(2) print("Finished running some_func.") thread1 = threading.Thread(target=some_func) thread1.start() print(threading.active_count())
上記のコード スニペットの機能を解析してみましょう。
-
threading
とtime
モジュールをインポートします。 - 関数
some_func
には、記述的なprint()
ステートメントがあり、2 秒間のスリープ操作が含まれていますtime.sleep(n)
は、関数をn
秒間スリープさせます。 - 次に、ターゲットを
some_func
としてスレッドthread_1
を定義します。threading.Thread(target=...)
はスレッド オブジェクトを作成します。 - 注: 関数呼び出しではなく、関数の名前を指定してください。
some_func()
ではなくsome_func
を使用してください。 - スレッド オブジェクトを作成しても、スレッドは開始されません。 スレッド オブジェクトで
start()
メソッドを呼び出すと実行されます。 - アクティブなスレッドの数を取得するには、
active_count()
関数を使用します。
Python スクリプトはメイン スレッドで実行されており、別のスレッド ( thread1
) を作成して関数some_func
を実行しているため、出力に見られるように、アクティブなスレッド数は 2 です。

# Output Running some_func... 2 Finished running some_func.
出力を詳しく見てみると、 thread1
の開始時に最初の print ステートメントが実行されていることがわかります。 ただし、スリープ操作中、プロセッサはメイン スレッドに切り替えて、スレッド 1 の実行が完了するのをthread1
ずに、アクティブなスレッドの数を出力します。

スレッドが実行を終了するのを待っています
thread1
の実行を終了させたい場合は、スレッドの開始後にjoin()
メソッドを呼び出すことができます。 そうすることで、スレッド 1 がメイン スレッドに切り替えずに実行を終了するのをthread1
ます。
import threading import time def some_func(): print("Running some_func...") time.sleep(2) print("Finished running some_func.") thread1 = threading.Thread(target=some_func) thread1.start() thread1.join() print(threading.active_count())
これで、アクティブなスレッド数を出力する前に、 thread1
の実行が終了しました。 したがって、メイン スレッドのみが実行されます。つまり、アクティブなスレッドの数は 1 です。
# Output Running some_func... Finished running some_func. 1
Python で複数のスレッドを実行する方法
次に、2 つの異なる関数を実行する 2 つのスレッドを作成しましょう。
ここでcount_down
は、引数として数値を取り、その数値からゼロまでカウントダウンする関数です。
def count_down(n): for i in range(n,-1,-1): print(i)
count_up
を定義します。これは、ゼロから指定された数までカウントする別の Python 関数です。
def count_up(n): for i in range(n+1): print(i)
構文
range(start, stop, step)
でrange()
関数を使用する場合、終点stop
はデフォルトで除外されます。– 特定の数値からゼロまでカウントダウンするには、負の
step
値 -1 を使用し、ゼロが含まれるようにstop
値を -1 に設定できます。– 同様に、
n
までカウントするには、stop
値をn + 1
に設定する必要があります。start
とstep
のデフォルト値はそれぞれ 0 と 1 であるため、range(n + 1)
を使用して 0 から n までのシーケンスを取得できます。
次に、関数count_down
とcount_up
をそれぞれ実行する 2 つのスレッド、 thread1
とthread2
を定義します。 両方の関数にprint
ステートメントとsleep
操作を追加します。
スレッド オブジェクトを作成するときは、ターゲット関数への引数をargs
パラメーターのタプルとして指定する必要があることに注意してください。 両方の関数 ( count_down
とcount_up
) は 1 つの引数を取ります。 値の後にカンマを明示的に挿入する必要があります。 これにより、後続の要素がNone
として推論されるため、引数が引き続きタプルとして渡されることが保証されます。
import threading import time def count_down(n): for i in range(n,-1,-1): print("Running thread1....") print(i) time.sleep(1) def count_up(n): for i in range(n+1): print("Running thread2...") print(i) time.sleep(1) thread1 = threading.Thread(target=count_down,args=(10,)) thread2 = threading.Thread(target=count_up,args=(5,)) thread1.start() thread2.start()
出力では:
- 関数
count_up
はthread2
2 で実行され、0 から 5 までカウントします。 -
count_down
関数は、スレッド 1 で実行され、10 から 0 までthread1
します。
# Output Running thread1.... 10 Running thread2... 0 Running thread1.... 9 Running thread2... 1 Running thread1.... 8 Running thread2... 2 Running thread1.... 7 Running thread2... 3 Running thread1.... 6 Running thread2... 4 Running thread1.... 5 Running thread2... 5 Running thread1.... 4 Running thread1.... 3 Running thread1.... 2 Running thread1.... 1 Running thread1.... 0
どちらも待機操作 (スリープ) を伴うため、 thread1
とthread2
が交互に実行されることがわかります。 count_up
関数が 5 までのカウントを終了すると、 thread2
はアクティブではなくなります。 したがって、 thread1
のみに対応する出力が得られます。
まとめ
このチュートリアルでは、Python の組み込みスレッド モジュールを使用してマルチスレッドを実装する方法を学習しました。 主な要点の概要は次のとおりです。
- Threadコンストラクターを使用して、スレッド オブジェクトを作成できます。 threading.Thread(target=<callable>,args=(<tuple of args>))を使用すると、 argsで指定された引数でターゲットcallable を実行するスレッドが作成されます。
- Python プログラムはメイン スレッドで実行されるため、作成するスレッド オブジェクトは追加のスレッドです。 active_count()関数を呼び出すと、任意のインスタンスでアクティブなスレッドの数が返されます。
- スレッド オブジェクトでstart()メソッドを使用してスレッドを開始し、 join()メソッドを使用して実行が終了するまで待機できます。
待機時間を微調整したり、別の I/O 操作を試したりすることで、追加の例をコーディングできます。 今後の Python プロジェクトでは必ずマルチスレッドを実装してください。 ハッピーコーディング!