Python スレッディング: はじめに

公開: 2022-10-25

このチュートリアルでは、Python の組み込みスレッドモジュールを使用して、Python のマルチスレッド機能を調べる方法を学習します。

プロセスとスレッドの基本から始めて、並行性と並列性の概念を理解しながら、Python でマルチスレッドがどのように機能するかを学びます。 次に、組み込みのthreadingモジュールを使用して、Python で 1 つ以上のスレッドを開始および実行する方法を学習します。

始めましょう。

プロセスとスレッド: 違いは何ですか?

プロセスとは

プロセス実行する必要があるプログラムのインスタンスです。

Python スクリプトや Chrome などの Web ブラウザからビデオ会議アプリケーションまで、何でもかまいません。 マシンでタスク マネージャーを起動し、[パフォーマンス] –> [ CPU ] に移動すると、現在 CPU コアで実行されているプロセスとスレッドを確認できます。

cpu-proc-スレッド

プロセスとスレッドについて

内部的に、プロセスには、プロセスに対応するコードとデータを格納する専用メモリがあります。

プロセスは 1 つ以上のスレッドで構成されます。 スレッドは、オペレーティング システムが実行できる最小の命令シーケンスであり、実行の流れを表します。

各スレッドには独自のスタックとレジスタがありますが、専用メモリはありません。 プロセスに関連付けられたすべてのスレッドがデータにアクセスできます。 したがって、データとメモリはプロセスのすべてのスレッドで共有されます。

プロセスとスレッド

N 個のコアを持つ CPU では、N 個のプロセスを同時に同時に実行できます。 ただし、同じプロセスの 2 つのスレッドを同時に実行することはできませんが、同時に実行することはできます。 次のセクションでは、同時実行と並列処理の概念について説明します。

これまでに学んだことに基づいて、プロセスとスレッドの違いを要約しましょう。

特徴プロセススレッド
メモリー専用メモリ共有メモリ
実行モード並列、同時同時; しかし平行ではない
によって処理される実行オペレーティング·システムCPython インタープリター

Python でのマルチスレッド

Python では、グローバル インタープリター ロック (GIL) により、任意の時点で1 つのスレッドのみがロックを取得して実行できることが保証されます。 実行するには、すべてのスレッドがこのロックを取得する必要があります。 これにより、特定の時点で 1 つのスレッドのみが実行されるようになり、同時マルチスレッド化が回避されます。

たとえば、同じプロセスの 2 つのスレッドt1t2について考えてみます。 スレッドは、 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())

上記のコード スニペットの機能を解析してみましょう。

  • threadingtimeモジュールをインポートします。
  • 関数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-ex

スレッドが実行を終了するのを待っています

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に設定する必要があります。 startstepのデフォルト値はそれぞれ 0 と 1 であるため、 range(n + 1)を使用して 0 から n までのシーケンスを取得できます。

次に、関数count_downcount_upをそれぞれ実行する 2 つのスレッド、 thread1thread2を定義します。 両方の関数にprintステートメントとsleep操作を追加します。

スレッド オブジェクトを作成するときは、ターゲット関数への引数をargsパラメーターのタプルとして指定する必要があることに注意してください。 両方の関数 ( count_downcount_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_upthread2 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

どちらも待機操作 (スリープ) を伴うため、 thread1thread2が交互に実行されることがわかります。 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 プロジェクトでは必ずマルチスレッドを実装してください。 ハッピーコーディング!