マルチスレッド プログラミングでは理解する必要のある基本的な概念がいくつかあり、これらはすべてのプログラミング言語に当てはまります。内容:
同時プログラミング
マルチタスクオペレーティングシステム
マルチスレッドとマルチプロセス
スレッドセーフティ
スレッドのライフサイクル
スレッドの種類
同時プログラミング
異なるプログラミングパラダイムは異なる影響を及ぼしますソフトウェアの観点。並行プログラミングでは、ソフトウェアをタスクとリソースの組み合わせと見なします。タスクは競合してリソースを共有し、リソースが満たされた場合はタスクを実行し、そうでない場合はリソースを待ちます。
並行プログラミングにより、ソフトウェアの理解と再利用が容易になり、特定のシナリオでパフォーマンスを大幅に向上させることができます。
マルチタスク オペレーティング システム
同時実行性を実現するには、まずオペレーティング システムのサポートが必要です。現在のオペレーティング システムのほとんどは、複数のタスクを「同時に」実行できるマルチタスク オペレーティング システムです。
マルチタスクはプロセスレベルまたはスレッドレベルで実行できます。
プロセスとは、メモリ内で実行されるアプリケーションを指します。各プロセスは独自の独立したメモリ空間を持っています。マルチタスク オペレーティング システムでは、これらのプロセスを「同時に」実行できます。
スレッドとは、順序が狂い、プロセス内で複数回実行されるコード ブロックを指します。複数のスレッドは「同時に」実行できるため、複数のスレッドは「同時」とみなされます。マルチスレッドの目的は、CPU リソースの使用を最大化することです。たとえば、JVM プロセスでは、すべてのプログラム コードがスレッドで実行されます。
ここで言う「同時性」と「同時性」はマクロ的な感覚で、実際にはミクロレベルで見るとプロセス/スレッドのローテーション実行に過ぎませんが、切り替え時間が非常に短いため、「並列性」が生じます。 " フィーリング。 。
マルチスレッドとマルチプロセス
オペレーティングシステムは各プロセスに異なるメモリブロックを割り当て、複数のスレッドがプロセスのメモリブロックを共有します。これがもたらす最も直接的な違いは、スレッドを作成するコストがプロセスを作成するコストよりもはるかに低いことです。
同時に、メモリブロックが異なるため、プロセス間の通信は比較的困難です。パイプ/名前付きパイプ、シグナル、メッセージキュー、共有メモリ、ソケットなどの手段を使用する必要があり、プロセス内でグローバル変数を共有するため、スレッド間の通信は簡単かつ高速です。
ただし、プロセスのスケジューリングはオペレーティング システムが担当し、デッドロック、飢餓、ライブロック、リソースの枯渇などを回避するためにスレッドのスケジューリングを独自に考慮する必要があり、ある程度の複雑さが追加されます。さらに、メモリはスレッド間で共有されるため、スレッドの安全性の問題も考慮する必要があります。
スレッドの安全性
スレッドはプロセス内でグローバル変数を共有するため、他のスレッドが共有変数を変更すると、このスレッドに影響を与える可能性があります。いわゆるスレッド セーフティ制約とは、関数が複数の同時スレッドによって繰り返し呼び出されたときに、常に正しい結果を生成する必要があることを意味します。スレッドの安全性を確保するための主な方法は、ロックを通じて共有変数への正しいアクセスを保証することです。
スレッドセーフよりも厳しい制約は「リエントランシー」です。つまり、あるスレッドでの実行中に関数が一時停止され、別のスレッドで呼び出され、元のスレッドに戻って実行を継続します。プロセス全体を通じて適切な実行が保証されます。再入可能は、通常、グローバル変数のローカルコピーを作成することによって保証されます。
スレッドライフサイクル
いわゆるxxライフサイクルは、実際にはオブジェクトの作成と破棄を含む状態図です。スレッドのライフサイクルは次の図に示されています:
各ステータスの説明は次のとおりです:
New。新しく作成されたスレッドは初期化されると、実行可能状態になります。
実行可能準備が整いました。スレッドのスケジュールを待っています。スケジュール設定後、実行状態に入ります。
ランニング。
ブロックされました。操作を一時停止し、ブロックを解除して実行可能状態に入り、再度スケジュールを待ちます。
死んだ。スレッドメソッドが実行後に戻るか異常終了します。
実行ブロック状態に入る場合は、次の 3 つの状況が考えられます:
同期: スレッドが同期ロックを取得したが、リソースが他のスレッドによってすでにロックされている場合、リソースが利用可能になるまでロック状態になります (取得の順序)。はロック キューによって制御されます)
Sleep: スレッドが sleep() または join() メソッドを実行した後、スレッドはスリープ状態に入ります。違いは、sleep は一定時間待機するのに対し、join は子スレッドの実行が完了するまで待機することです。もちろん、結合には「タイムアウト期間」を指定することもできます。意味的に言えば、2 つのスレッド a と b が a で b.join() を呼び出す場合、それは 1 つのスレッドにマージ (結合) することと同じです。最も一般的な状況は、すべての子スレッドをメインスレッドに結合することです。
Waiting: スレッド内で wait() メソッドを実行した後、スレッドは Waiting 状態になり、他のスレッドからの通知を待ちます。
スレッドの種類
メインスレッド: プログラムが開始されると、オペレーティングシステム (OS) によってプロセスが作成され、同時にスレッドがすぐに実行されます。このスレッドは通常、プログラムのメインスレッド (Main) と呼ばれます。糸)。すべてのプロセスには少なくとも 1 つのメイン スレッドがあり、通常はメイン スレッドが最後にシャットダウンされます。
サブスレッド: プログラム内で作成された他のスレッドは、メインスレッドに対するこのメインスレッドのサブスレッドです。
デーモンスレッド: デーモンスレッド、スレッドの識別。デーモン スレッドは、JVM のガベージ コレクション スレッドなどの他のスレッドにサービスを提供します。すべてのデーモン スレッドが残ると、プロセスは終了します。
フォアグラウンド スレッド: デーモン スレッドに関連する他のスレッドは、フォアグラウンド スレッドと呼ばれます。
Python のマルチスレッドのサポート
仮想マシンレベル
Python 仮想マシンは、GIL (グローバル インタープリター ロック、グローバル インタープリター ロック) を使用して、共有リソースへのスレッドのアクセスを相互に排除しており、一時的にマルチプロセッサを利用できなくなります。
言語レベル
言語レベルでは、Python は、スレッド、スレッド、キューなどのマルチスレッド関連モジュールを適切にサポートしています。スレッドの作成、ミューテックス ロック、セマフォ、同期などの機能を簡単にサポートできます。
スレッド: マルチスレッドの基礎となるサポートモジュール。通常は推奨されません。
threading: スレッドをカプセル化し、一部のスレッド操作をオブジェクト化し、次のクラスを提供します:
Thread スレッド クラス
Timer は Thread に似ていますが、実行を開始する前に一定時間待機する必要があります
Lock ロック プリミティブ
RLock はリエントラント ロックです。単一のスレッドが既に取得済みのロック
条件条件変数を再取得できるようにします。これにより、スレッドが停止し、他のスレッドが特定の「条件」を満たすのを待つことができます
イベント 一般条件変数。複数のスレッドがイベントの発生を待つことができます。イベントの発生後、すべてのスレッドがアクティブになります
セマフォは、ロックを待機しているスレッドに「待機室」のような構造を提供します
BoundedSemaphore はセマフォに似ていますが、それを超えることはできません。初期値
Queue: マルチプロデューサー (Producer) とマルチコンシューマー (Consumer) キューを実装し、ロック プリミティブをサポートし、複数のスレッド間で優れた同期サポートを提供できます。提供されるクラス:
Queue キュー
LifoQueue 後入れ先出し (LIFO) キュー
PriorityQueue 優先キュー
このうち、Thread クラスはメインのスレッド クラスであり、プロセス インスタンスを作成できます。このクラスによって提供される関数は次のとおりです:
getName(self) スレッドの名前を返します
isAlive(self) スレッドがまだ実行中かどうかを示すブール型フラグ
isDaemon(self) スレッドのデーモン フラグを返します
join(self , timeout=None) プログラムはスレッドが終了するまでハングします。タイムアウトが指定されると、タイムアウト秒までブロックされます
run(self) スレッドの関数 function を定義します
setDaemon(self, daemonic) ) スレッドのデーモンフラグをデーモンに設定します
setName(self, name) スレッドの名前を設定します
start(self) スレッドの実行を開始します
サードパーティのサポート
パフォーマンスを特に重視する場合は、次のことができますいくつかの「マイクロスレッド」実装も検討してください:
スタックレス Python: マイクロスレッドのサポートを提供する Python の拡張バージョン。マイクロスレッドは軽量のスレッドであり、複数のスレッド間の切り替えに時間がかかり、占有するリソースが少なくなります。
greenlet: スタックレスの副産物で、マイクロスレッドを「タスクレット」と呼びます。タスクレットは擬似同時実行で実行され、同期データ交換にチャネルを使用します。 「グリーンレット」は、スケジューリングのない、より原始的なマイクロスレッドの概念です。マイクロスレッド スケジューラを自分で構築することも、グリーンレットを使用して高度な制御フローを実装することもできます。
次のセクションでは、Python でスレッドの作成と開始を開始します。