GOランタイムは、データ構造の洗練された調整、ロックメカニズム、ゴロウチンスケジューリング、およびゴルチン間の効率的で公正な、およびデッドロックのない通信を提供する同期を通じて内部的にチャネル操作を処理します。
GOのチャネルは、主にデータ構造「HCHAN」として実装されています。これは、バッファー、待機中の送信者とレシーバーのキュー、同期プリミティブ、チャネルが閉じているかどうかなどのメタ情報を含むチャネルの状態をカプセル化します。チャネルが作成されると、「hchan」のインスタンスがヒープに割り当てられ、チャネルを表す値はこの構造を指し、複数のゴルチンが参照して同時に使用できます。
コアでは、チャネルは次のとおりです。
- それぞれ要素を送信して受信する場所を追跡するスライスとインデックス「sendx」と「recvx」で表されるバッファーチャネルの円形バッファー。バッファサイズはチャネル作成時に固定されており、送信されたがまだ受信されていない要素を保存するキューとして機能します。
- 送信時にブロックされたゴルチン(「sendq」)と受信( `recvq`)を管理するための2つのFIFO待機キュー(リンクリスト)。これらのキューは、「sudog」構造を保持します。これは、ブロックされたゴロウチンと、送信する値や値を受信する場所へのポインターなどのチャネル操作の関連データを表します。
- チャネルの内部構造への同時アクセスを保護するためのミューテックスロック。送信および受信操作が一貫性を維持し、同時アクセスの下でチャネルの状態を破壊しないようにします。
###チャネル送信操作
ゴルウチンがチャネルに値を送信しようとしたとき( `ch e操作
受信操作( `lexity:
1。ロック取得:チャネルのミューテックスがロックされています。
2。待機中の送信者を確認してください:「sendq」で待機中の送信者がいる場合:
- 受信者は、送信者の「sudog」から直接値を取得します。
- 送信者はデキュードされ、マークされているRunnableです。
- 両方のゴルチンは、バッファリングせずにすぐに進みます。
3.バッファーチャネルのバッファーを確認してください:送信者が待っていない場合:
- チャネルは、バッファに要素が含まれているかどうかを確認します。
- その場合、バッファー位置 `buf [recvx]`から要素がコピーされます。
- `recvx`インデックスが増加し、「qcount」が減少します。
- ロックがリリースされ、レシーバーはブロックせずに継続します。
4。受信機のブロック:バッファーが空で、送信者が待っていない場合:
- レシーバーのゴルチンは「sudog」として表されます。
- `recvq`でエンキューされています。
- 受信機は、送信操作がブロック解除されるまでスケジューラに駐車されます。
5。閉じたチャネルの受信:チャネルが閉じていてバッファーが空の場合:
- [要素タイプのゼロ値を返します。
- 受信機はブロックせず、受信操作から返された2番目のブール値によって閉じた状態を検出できます。
6.公平性と順序:待機中のレシーバーは、公平性を維持するためにFIFOを除去し、スケジューリングは秩序の制約を尊重しますが、スケジューラの動作による厳格なタイミング順序を保証しません。
###駐車とスケジューリングの統合
チャネルは、3つのエンティティを使用してゴルチンのライフサイクルを管理するGoのゴルウチンスケジューラと密接に統合されています。
-G(Goroutine):軽量、ユーザーレベルのスレッド。
-M(マシン):ゴルチンを実行するOSスレッド。
-P(プロセッサ):Goコードを実行するために必要な実行可能なゴルチンとリソースのローカルキューを保持します。
チャネル操作がブロックされた場合(送信または受信)、ゴルウチン:
- 待っているとマークされています。
- 所有「P」のローカルランキューから削除されます。
- それぞれのチャネルキュー(「sendq」または「recvq」)の「sudog」構造を介してリンクされています。
- スケジューラは、別の実行可能なゴルウチンを選択して、その「P」で実行します。
ブロックされた操作が完了する準備ができたとき(たとえば、対応する送信または受信が発生します):
- 待機中のゴルウチンは、チャンネルキューからデキュエットされています。
- マークされた実行可能。
- スケジューリングのためにローカルまたはグローバルの実行キューに戻ります。
-Goroutineは最終的に実行を再開します。
このデザインは、忙しい待機を避け、従来のOSスレッドブロッキングと比較して最小限のオーバーヘッドで効率的なコンテキストスイッチングを保証します。
###チャネル閉鎖操作
チャネルを閉じると、同じミューテックスによって保護された「hchan」に「閉じた」フラグが設定されます。チャネルを閉じると、次のようになります。
- 送信を試みた場合、すべてのブロックされた送信者がパニックになります。
- すべてのブロックされた受信機は、残りのバッファー値を受け取り、次にゼロ値を受信するためにFIFO順序でブロックされていません。
- ブロッキングせずにすぐに戻るゼロ値をさらに受信します。
- 閉じたチャネルパニックをすぐに送信します。
- 緊密な動作は、人種の状態を避けるために、ミューテックスを介したチャネル操作と同期されます。
###内部データ構造
-HCHAN:中央構造体保持:
- 「buf」(バッファー配列ポインター)
- `dataqsiz`(バッファサイズ)
- `qcount`(現在バッファリングされている要素の数)
- `sendx`、` recvx`(バッファインデックス)
- `sendq`、` recvq`(ブロックされた送信者とレシーバーを待機)
- 「閉じた」フラグ
- 埋め込まれたミューテックス(状態を保護し、複数の並行操作を調整するため)。
-dudog:チャネル操作を待っているゴルウチンを表す内部ランタイム構造。ホールド:
- ゴルウチンへのポインター。
- 送信または受信されるデータへのポインター。
- 待機キューの次の「sudog」へのリンク。
-Waitq:ウェイターを送信する( `sendq`)またはウェイターを受け取る(` recvq`)を表すsudogsのリンクリスト。 FIFOの公平性を確保します。
###ステートメントとチャネルを選択します
Goの「Select」ステートメントは、複数のチャネル操作が同時に競合するため、複雑さを導入します。ランタイムは、各選択ケースの複数の待機キューを管理し、複数のチャネルからオフラインで選択することには次のものが含まれます。
- 各ケースで非ブロッキング操作を試みます。
- 何も続けない場合、すべての関連するキューでゴルチンをenqueueします。
- 1つの操作が進むと、他のウェイターが削除され、ゴルチンがブロックされます。
この選択的な待機メカニズムは、公平性と効率を維持するために、ランタイムスケジューラと深く統合されています。
###パフォーマンスと並行性の考慮事項
チャネル設計は、安全性とパフォーマンスのバランスをとるために努力しています。
-Mutex Locking:一貫性を保証しますが、非常に高い競合の下でボトルネックになる可能性があります。ランタイムは、状態を原子的に更新するために、ロックを一時的に保持し続けます。
- ダイレクトハンドオフ:可能な場合はバッファオーバーヘッドを回避し、ゴルチン間のより速い通信を可能にします。
- 駐車と包含:OSスレッドと比較して、コンテキストの切り替えオーバーヘッドを最小化します。
- 公平性:飢starを防ぐためにFIFOキューを介して実施されます。
- キャッシュとローカリティ:未分割のゴルウチンは、異なるプロセッサ(「P」)で再開でき、キャッシュミスにつながる可能性があります。
- 雷の群れの問題:チャンネルまたは放送を閉じると、多くのゴルチンが同時に目覚める可能性があります。これは、スケジューラが慎重に処理する必要があります。
チャネルは、Goroutineスケジューリングメカニズムとロックで保護されたリングバッファーを結びつけるエレガントな並行性プリミティブを表し、GOプログラムの安全性、順序、効率を提供します。
GOチャネル操作の内部により、データ構造、ロック、およびランタイムスケジューラ間の緊密な結合が明らかになり、ユーザーレベルの軽量スレッドに同期を埋め込み、OSカーネルの相互作用のない豊富な通信パターンを可能にします。このアプローチは、GOの同時性モデルを区別し、プログラマーの観点からは効率的で簡単な使用が可能な主な理由です。