例を使用した Python でのマルチスレッド化:Python で GIL を学ぶ
Python プログラミング言語を使用すると、マルチプロセッシングまたはマルチスレッドを使用できます。このチュートリアルでは、Python でマルチスレッド アプリケーションを作成する方法を学習します。
スレッドとは
スレッドは、並行プログラミングの実行単位です。マルチスレッドは、CPU が 1 つのプロセスの多くのタスクを同時に実行できるようにする技術です。これらのスレッドは、プロセス リソースを共有しながら個別に実行できます。
プロセスとは?
プロセスは基本的に実行中のプログラムです。コンピュータでアプリケーション (ブラウザやテキスト エディタなど) を起動すると、オペレーティング システムによって プロセスが作成されます。
Python のマルチスレッドとは?
Python でのマルチスレッド プログラミングは、プロセス内の複数のスレッドがデータ空間をメインスレッドと共有するよく知られた手法であり、スレッド内での情報の共有と通信を簡単かつ効率的にします。スレッドはプロセスよりも軽量です。マルチスレッドは、プロセス リソースを共有しながら個別に実行できます。マルチスレッドの目的は、複数のタスクと機能セルを同時に実行することです。
このチュートリアルでは、
- スレッドとは?
- プロセスとは?
- マルチスレッドとは?
- マルチプロセッシングとは?
- Python マルチスレッドとマルチプロセッシング
- マルチスレッドを使用する理由
- Python マルチスレッディング
- Thread および Threading モジュール
- スレッド モジュール
- スレッド化モジュール
- デッドロックと競合状態
- スレッドの同期
- GIL とは?
- なぜ GIL が必要だったのですか?
マルチプロセッシングとは
マルチプロセッシングを使用すると、関連のない複数のプロセスを同時に実行できます。これらのプロセスはリソースを共有せず、IPC を介して通信します。
Python マルチスレッド vs マルチプロセッシング
プロセスとスレッドを理解するには、次のシナリオを考慮してください。コンピューター上の .exe ファイルはプログラムです。開くと、OS がメモリにロードし、CPU が実行します。現在実行中のプログラムのインスタンスは、プロセスと呼ばれます。
すべてのプロセスには 2 つの基本的なコンポーネントがあります:
現在、プロセスには、スレッドと呼ばれる 1 つ以上のサブパーツを含めることができます。 これは、OS アーキテクチャに依存します。スレッドは、オペレーティング システムによって個別に実行できるプロセスのセクションと考えることができます。
つまり、OS が独立して実行できる一連の命令です。 1 つのプロセス内のスレッドは、そのプロセスのデータを共有し、並列処理を促進するために連携するように設計されています。
マルチスレッドを使用する理由
マルチスレッドを使用すると、アプリケーションを複数のサブタスクに分割し、これらのタスクを同時に実行できます。マルチスレッドを適切に使用すると、アプリケーションの速度、パフォーマンス、およびレンダリングをすべて改善できます。
Python マルチスレッド
Python は、マルチプロセッシングとマルチスレッドの両方の構造をサポートしています。このチュートリアルでは、主に マルチスレッド の実装に焦点を当てます python を使用したアプリケーション。 Python でスレッドを処理するために使用できる 2 つの主要なモジュールがあります:
<オール>
スレッド モジュール、および
スレッド化 モジュール
ただし、Python には、グローバル インタープリター ロック (GIL) と呼ばれるものもあります。パフォーマンスが大幅に向上するわけではなく、低下することさえあります 一部のマルチスレッド アプリケーションのパフォーマンス。このチュートリアルの以降のセクションですべてを学習します。
Thread および Threading モジュール
このチュートリアルで学習する 2 つのモジュールは、スレッド モジュールです。 threading モジュール .
ただし、thread モジュールは長い間廃止されてきました。 Python 3 以降、廃止され、__thread としてのみアクセス可能になりました。 後方互換性のため。
より高いレベルのスレッド化を使用する必要があります 展開する予定のアプリケーションのモジュール。スレッド モジュールは、ここでは教育目的でのみ取り上げています。
スレッド モジュール
このモジュールを使用して新しいスレッドを作成する構文は次のとおりです:
thread.start_new_thread(function_name, arguments)
さて、コーディングを開始するための基本的な理論を説明しました。したがって、IDLE またはメモ帳を開いて、次のように入力してください:
import time
import _thread
def thread_test(name, wait):
i = 0
while i <= 3:
time.sleep(wait)
print("Running %s\n" %name)
i = i + 1
print("%s has finished execution" %name)
if __name__ == "__main__":
_thread.start_new_thread(thread_test, ("First Thread", 1))
_thread.start_new_thread(thread_test, ("Second Thread", 2))
_thread.start_new_thread(thread_test, ("Third Thread", 3))
ファイルを保存し、F5 キーを押してプログラムを実行します。すべてが正しく行われた場合、次の出力が表示されます:
競合状態とその処理方法については、以降のセクションで詳しく説明します
コードの説明
<オール>
これらのステートメントは、Python スレッドの実行と遅延を処理するために使用される時間とスレッド モジュールをインポートします。
ここで、thread_test という関数を定義しました。 start_new_thread によって呼び出されます 方法。関数は while ループを 4 回実行し、それを呼び出したスレッドの名前を出力します。反復が完了すると、スレッドの実行が終了したことを示すメッセージが出力されます。
これはプログラムのメイン セクションです。ここでは、 start_new_thread を呼び出すだけです thread_test を使用したメソッド これにより、引数として渡した関数の新しいスレッドが作成され、実行が開始されます。これを置き換えることができることに注意してください (thread_ test) をスレッドとして実行する他の関数と組み合わせて使用します。
スレッド化モジュール
このモジュールは、Python でのスレッド化の高レベルの実装であり、マルチスレッド アプリケーションを管理するためのデファクト スタンダードです。 thread モジュールと比較すると、幅広い機能を提供します。
センター> センター> フィギュア>
以下は、このモジュールで定義されているいくつかの便利な関数のリストです:
関数名 | 説明 |
activeCount() | スレッドの数を返します まだ生きているオブジェクト |
currentThread() | Thread クラスの現在のオブジェクトを返します。 |
enumerate() | すべてのアクティブな Thread オブジェクトを一覧表示します。 |
isDaemon() | スレッドがデーモンの場合は true を返します。 |
isAlive() | スレッドがまだ生きている場合は true を返します。 |
| スレッド クラス メソッド |
start() | スレッドのアクティビティを開始します。複数回呼び出すと実行時エラーがスローされるため、スレッドごとに 1 回だけ呼び出す必要があります。 |
run() | このメソッドはスレッドのアクティビティを示し、Thread クラスを拡張するクラスによってオーバーライドできます。 |
join() | join() メソッドが呼び出されたスレッドが終了するまで、他のコードの実行をブロックします。 |
バックストーリー:スレッド クラス
threading モジュールを使用してマルチスレッド プログラムのコーディングを開始する前に、Thread クラスについて理解することが重要です。スレッド クラスは、Python でテンプレートとスレッドの操作を定義する主要なクラスです。
マルチスレッドの Python アプリケーションを作成する最も一般的な方法は、Thread クラスを拡張し、その run() メソッドをオーバーライドするクラスを宣言することです。
Thread クラスは、要約すると、別のスレッドで実行されるコード シーケンスを意味します。
したがって、マルチスレッド アプリを作成するときは、次のことを行います。
<オール> Thread クラスを拡張するクラスを定義する
__init__ をオーバーライドする コンストラクタ
run() をオーバーライドする メソッド
スレッド オブジェクトが作成されると、start() メソッドを使用して、このアクティビティの実行を開始し、join() メソッドを使用して、現在のアクティビティが終了するまで他のすべてのコードをブロックできます。
では、threading モジュールを使用して前の例を実装してみましょう。再び、IDLE を起動して次のように入力します:
import time
import threading
class threadtester (threading.Thread):
def __init__(self, id, name, i):
threading.Thread.__init__(self)
self.id = id
self.name = name
self.i = i
def run(self):
thread_test(self.name, self.i, 5)
print ("%s has finished execution " %self.name)
def thread_test(name, wait, i):
while i:
time.sleep(wait)
print ("Running %s \n" %name)
i = i - 1
if __name__=="__main__":
thread1 = threadtester(1, "First Thread", 1)
thread2 = threadtester(2, "Second Thread", 2)
thread3 = threadtester(3, "Third Thread", 3)
thread1.start()
thread2.start()
thread3.start()
thread1.join()
thread2.join()
thread3.join()
これは、上記のコードを実行したときの出力です:
コードの説明
<オール> この部分は、前の例と同じです。ここでは、Python スレッドの実行と遅延を処理するために使用される時間とスレッド モジュールをインポートします。
この部分では、Thread を継承または拡張する threadtester というクラスを作成します。 threading モジュールのクラス。これは、Python でスレッドを作成する最も一般的な方法の 1 つです。ただし、コンストラクタと run() のみをオーバーライドする必要があります アプリのメソッド。上記のコード サンプルでわかるように、__init__ メソッド (コンストラクター) がオーバーライドされました。同様に、 run() もオーバーライドしました 方法。スレッド内で実行するコードが含まれています。この例では、thread_test() 関数を呼び出しています。
これは i の値を取る thread_test() メソッドです。 引数として、各反復でそれを 1 減らし、i が 0 になるまで残りのコードをループします。各反復で、現在実行中のスレッドの名前を出力し、待機秒の間スリープします (これも引数として使用されます)。 ).
thread1 =threadtester(1, “First Thread”, 1) ここでは、スレッドを作成し、__init__ で宣言した 3 つのパラメーターを渡します。最初のパラメータはスレッドの ID、2 番目のパラメータはスレッドの名前、3 番目のパラメータはカウンタで、while ループを実行する回数を決定します。
thread2.start()start メソッドは、スレッドの実行を開始するために使用されます。内部的に、start() 関数はクラスの run() メソッドを呼び出します。
thread3.join() join() メソッドは他のコードの実行をブロックし、呼び出されたスレッドが終了するまで待機します。
ご存知のように、同じプロセスにあるスレッドは、そのプロセスのメモリとデータにアクセスできます。その結果、複数のスレッドがデータを同時に変更またはアクセスしようとすると、エラーが忍び寄る可能性があります。
次のセクションでは、スレッドが既存のアクセス トランザクションをチェックせずにデータとクリティカル セクションにアクセスするときに発生する可能性があるさまざまな種類の問題について説明します。
デッドロックと競合状態
デッドロックと競合状態について学習する前に、並行プログラミングに関連するいくつかの基本的な定義を理解しておくと役に立ちます。
- クリティカル セクション共有変数にアクセスまたは変更するコードの断片であり、アトミック トランザクションとして実行する必要があります。
- コンテキスト スイッチは、あるタスクから別のタスクに変更する前にスレッドの状態を保存して、後で同じ時点から再開できるようにするために CPU が従うプロセスです。
デッドロック
デッドロックは、Python で並行/マルチスレッド アプリケーションを作成する際に開発者が直面する最も恐れられる問題です。デッドロックを理解する最善の方法は、食事の哲学者問題として知られる古典的なコンピュータ サイエンスの例の問題を使用することです。
食事の哲学者の問題は次のとおりです。
図に示すように、5 人の哲学者が 5 皿のスパゲッティ (パスタの一種) と 5 つのフォークを備えた円卓に座っています。