詳細な Golang チャネル: 実装原則とパフォーマンス最適化の提案
Golang のチャネルは、CSP 同時実行モデルの重要なコンポーネントであり、Goroutine 間の通信のブリッジです。 Channel は Golang で頻繁に使用されるため、その内部実装原理を深く理解することが重要です。この記事では、Go 1.13 ソース コードに基づいて Channel の基礎となる実装を分析します。
チャンネルの基本的な使い方
チャネルの実装を正式に分析する前に、その基本的な使用法を確認してみましょう:
package main import "fmt" func main() { c := make(chan int) go func() { c <- 1 // 发送操作 }() x := <-c // 接收操作 fmt.Println(x) }
このコードは、チャネルの 2 つの基本操作を示しています。
- 送信操作:
c
- 受信操作:
x :=
チャネルはバッファ付きチャネルとバッファなしチャネルに分かれています。上記のコードは、バッファリングされていないチャネルを使用しています。バッファリングされていないチャネルでは、現在データを受信している他の Goroutine が存在しない場合、送信側は send ステートメントでブロックされます。
チャネルを初期化するときにバッファ サイズを指定できます。たとえば、make(chan int, 2)
はバッファ サイズを 2 に指定します。バッファがいっぱいになる前に、送信者は受信者の準備が整うのを待たずに、ブロックせずにデータを送信できます。ただし、バッファがいっぱいの場合でも、送信者はブロックします。
チャネルの基礎となる実装関数
チャネルのソース コードに入る前に、Golang でチャネルの具体的な実装場所を見つける必要があります。 Channel を使用すると、runtime.makechan
、runtime.chansend
、runtime.chanrecv
などの基礎となる関数が実際に呼び出されます。
go tool compile -N -l -S hello.go
コマンドを使用してコードをアセンブリ命令に変換することも、オンライン ツール Compiler Explorer (例: go.godbolt.org/z/3xw5Cj) を使用することもできます。組み立て説明書を分析すると、次のことがわかります。
-
make(chan int)
はruntime.makechan
関数に対応します。 c は <code>runtime.chansend
関数に対応します。x := は <code>runtime.chanrecv
関数に対応します。
これらの関数の実装は、Go ソース コードの runtime/chan.go
ファイルにあります。
チャネル構造
make(chan int)
はコンパイラによって runtime.makechan
関数に変換され、その関数シグネチャは次のとおりです:
func makechan(t *chantype, size int) *hchan
このうち、t *chantype
は Channel 要素の型、size int
はユーザー指定のバッファ サイズ (指定しない場合は 0)、戻り値は *hchan
です。 hchan
は Golang のチャネルの内部実装構造であり、次のように定義されます。
type hchan struct { qcount uint // 缓冲区中已放入元素的数量 dataqsiz uint // 用户构造Channel时指定的缓冲区大小 buf unsafe.Pointer // 缓冲区 elemsize uint16 // 缓冲区中每个元素的大小 closed uint32 // Channel是否关闭,==0表示未关闭 elemtype *_type // Channel元素的类型信息 sendx uint // 缓冲区中发送元素的索引位置(发送索引) recvx uint // 缓冲区中接收元素的索引位置(接收索引) recvq waitq // 等待接收的Goroutine列表 sendq waitq // 等待发送的Goroutine列表 lock mutex }
の属性は、大きく 3 つのカテゴリに分類されます。 hchan
-
バッファ関連属性:
buf
、dataqsiz
、qcount
など。チャネルのバッファ サイズが 0 以外の場合、バッファは受信するデータを格納するために使用され、リング バッファを使用して実装されます。 -
待機キュー関連属性:
recvq
にはデータの受信を待機している Goroutine が含まれ、sendq
にはデータの送信を待機している Goroutine が含まれます。waitq
二重リンクリストを使用して実装されます。 -
その他の属性:
lock
、elemtype
、closed
など。
makechan
関数は主に、バッファーや hchan
などの属性の正当性チェックとメモリ割り当てを実行しますが、ここでは詳しく説明しません。
hchan
属性の簡単な分析に基づいて、バッファーと待機キューという 2 つの重要なコンポーネントがあることがわかります。 hchan
のすべての動作と実装は、これら 2 つのコンポーネントを中心に展開します。
チャンネルデータ送信
チャンネルの送信プロセスと受信プロセスは非常に似ています。まず、チャネル (例: c ) の送信プロセスを分析します。
が Channel にデータを送信しようとしたとき、recvq
キューが空でない場合は、データの受信を待機している Goroutine が recvq
ヘッダーから取り出され、データが直接 Goroutine に送信されます。コードは次のとおりです:
package main import "fmt" func main() { c := make(chan int) go func() { c <- 1 // 发送操作 }() x := <-c // 接收操作 fmt.Println(x) }
recvq
データの受信を待機している Goroutine が含まれています。 Goroutine が受信操作 (x := など) を使用するとき、この時点で <code>sendq
が空でない場合は、Goroutine が sendq
から取得され、データがそれに送信されます。
recvq
が空の場合は、現時点でデータの受信を待機しているゴルーチンが存在しないことを意味し、チャネルはデータをバッファーに入れようとします:
func makechan(t *chantype, size int) *hchan
このコードの機能は非常に単純で、データをバッファに入れることです。このプロセスにはリング バッファの操作が含まれます。dataqsiz
はユーザー指定のバッファ サイズを表します (指定しない場合、デフォルトは 0)。
バッファリングされていないチャネルが使用されている場合、またはバッファがいっぱいである (c.qcount == c.dataqsiz
) 場合、送信されるデータと現在のゴルーチンは sudog
オブジェクトにパッケージ化され、sendq
に配置され、現在のゴルーチンはGoroutine は待機するように設定されます ステータス:
type hchan struct { qcount uint // 缓冲区中已放入元素的数量 dataqsiz uint // 用户构造Channel时指定的缓冲区大小 buf unsafe.Pointer // 缓冲区 elemsize uint16 // 缓冲区中每个元素的大小 closed uint32 // Channel是否关闭,==0表示未关闭 elemtype *_type // Channel元素的类型信息 sendx uint // 缓冲区中发送元素的索引位置(发送索引) recvx uint // 缓冲区中接收元素的索引位置(接收索引) recvq waitq // 等待接收的Goroutine列表 sendq waitq // 等待发送的Goroutine列表 lock mutex }
goparkunlock
は入力ミューテックスのロックを解除し、現在の Goroutine を一時停止して待機状態に設定します。 gopark
と goready
はペアで表示され、相互演算です。
ユーザーの観点から見ると、gopark
を呼び出した後、データを送信するコード ステートメントがブロックされます。
チャンネルデータ受信
チャンネルの受信プロセスは基本的に送信プロセスと似ているため、ここでは詳しく説明しません。受信処理におけるバッファに関する動作の詳細については後述する。
Channel の送受信プロセス全体が runtime.mutex
を使用してロックされることに注意してください。 runtime.mutex
は、ランタイム関連のソース コードで一般的に使用される軽量ロックです。プロセス全体が最も効率的なロックフリーのソリューションではありません。 Golang のロックフリー チャネルに関する問題があります: go/issues#8899。
チャネルリングバッファの実装
チャネルはリング バッファを使用して、書き込まれたデータをキャッシュします。リング バッファには多くの利点があり、固定長 FIFO キューの実装に最適です。
Channel でのリング バッファの実装は次のとおりです:
hchan
には、recvx
と sendx
という 2 つのバッファー関連変数があります。 sendx
はバッファ内の書き込み可能なインデックスを表し、recvx
はバッファ内の読み取り可能なインデックスを表します。 recvx
と sendx
の間の要素は、通常どおりバッファに入れられたデータを表します。

buf[recvx]
を直接使用してキューの最初の要素を読み取り、buf[sendx] = x
を使用して要素をキューの最後に置くことができます。
バッファ書き込み
バッファがいっぱいでない場合、バッファにデータを入れる操作は次のとおりです:
package main import "fmt" func main() { c := make(chan int) go func() { c <- 1 // 发送操作 }() x := <-c // 接收操作 fmt.Println(x) }
chanbuf(c, c.sendx)
は c.buf[c.sendx]
と同等です。上記のプロセスは非常に簡単で、データをバッファーの場所 sendx
にコピーするだけです。
次に、sendx
を次の位置に移動します。 sendx
が最後の位置に到達すると、0 に設定されます。これは、一般的なエンドツーエンドのアプローチです。
バッファ読み取り
バッファがいっぱいでない場合、sendq
も空でなければなりません (バッファがいっぱいでない場合、データを送信するゴルーチンはキューに入れられず、データを直接バッファに入れるためです)。このとき、チャネル chanrecv
の読み取りロジックは比較的単純で、データをバッファから直接読み取ることもできます。これは、基本的に上記のバッファ書き込みと同じです。 recvx
内に待機中のGoroutineがある場合、この時点でバッファはフルになっている必要があります。このときのChannelの読み込みロジックは以下の通りです: sendq
func makechan(t *chantype, size int) *hchan
は、データを受け取る変数に対応するアドレスです (たとえば、ep
の場合、x := は <code>ep
のアドレスです)。 x
は、sg
から取得された最初の sendq
を表します。コード内: sudog
- は、バッファー内の現在読み取り可能な要素を受け取り側の変数のアドレスにコピーすることを意味します。
typedmemmove(c.elemtype, ep, qp)
- は、
typedmemmove(c.elemtype, qp, sg.elem)
のGoroutineによって送信されるのを待っているデータをバッファにコピーすることを意味します。sendq
は後で実行されるため、キューの最後にあるrecv
にデータを配置することと同じになります。sendq
内の要素をキューの最後にコピーすることで、FIFO (先入れ先出し) を実装します。 。 sendq
概要
チャネルは、Golang で最もよく使用される機能の 1 つです。そのソース コードを理解することは、チャネルをよりよく使用し、理解するのに役立ちます。同時に、過度に迷信的になって Channel のパフォーマンスに依存しないでください。Channel の現在の設計には、最適化の余地がまだたくさんあります。
最適化の提案:
- パフォーマンスを向上させるには、より軽量なロック メカニズムまたはロックフリー スキームを使用します。
- バッファ管理を最適化し、メモリ割り当てとコピー操作を削減します。
Leapcell: Golang Web アプリケーションに最適なサーバーレス プラットフォーム

最後に、Go サービスのデプロイに非常に適したプラットフォームをお勧めします: Leapcell
- 多言語サポート: JavaScript、Python、Go、または Rust の開発をサポートします。
- 無制限のプロジェクトを無料で展開: 使用した分だけ支払い、リクエストや手数料はかかりません。
- 非常にコスト効率が高い: 従量課金制で、アイドル料金はかかりません。例: 25 ドルは、平均応答時間 60 ミリ秒で 694 万件のリクエストをサポートします。
- スムーズな開発者エクスペリエンス: 簡単なセットアップのための直感的な UI、完全に自動化された CI/CD パイプラインと GitOps の統合により、実用的な洞察が得られます。
- 簡単なスケーラビリティと高いパフォーマンス: 自動的にスケーリングして高い同時実行性を簡単に処理し、運用上のオーバーヘッドをゼロにし、構築に集中します。

詳細については、ドキュメントを確認してください。
リープセル Twitter: https://www.php.cn/link/7884effb9452a6d7a7a79499ef854afd
以上がGo チャンネルのロック解除: 仕組みの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

fatestinggocodewithinit functions、useexplicitsetupfunctionsurseSorseparatet fileStoavoidepencyonInitonitisideEffects.1)useexplicitsetupfuncontrollglobalbariaveInitialization.2)createSeparateSteSteSteStobypassInit funtedtententen

Go'serrorhandlingReturnserrorsasasvalues、javaandpython whichuseexceptions.1)go'smethodensuresexpliciterror handling

効果的なインターフェイスリングミニマル、クリア、およびプロモテスルーシューリング。1)インターフェイスForfforfibilityOfimplementation.2)interfacesforact forabstractiontoswapimplementations withingingcallingcode.3)設計の快適性を発信すること

集中型エラー処理は、GO言語でのコードの読みやすさと保守性を向上させることができます。その実装方法と利点には、次のものが含まれます。1。ビジネスロジックからロジックを個別に処理し、コードを簡素化します。 2。中央の取り扱いによるエラー処理の一貫性を確保します。 3. DeferとRecoverを使用してパニックをキャプチャおよび処理して、プログラムの堅牢性を高めます。

Ingo、AlternativestoinititionCustomInitializationAndSingletons.1)CustomInitializationAltionsionAlowoveroveroveroveroveroveroveroveroveroveroveroveroveroveroveroverover curs、beantefordedorcontionalsetups.2)singletonsensureone-initializatializatializatialent

gohandlesinterfacesandtypeassertionseffectivivivivivity、強化された柔軟性と耐毒性を強化します

言語エラー処理は、エラーとエラーを介してより柔軟になり、読みやすくなります。 1.エラーは、エラーが指定されたエラーと同じであり、エラーチェーンの処理に適しているかどうかを確認するために使用されます。 2.エラー。エラータイプを確認するだけでなく、エラーを特定のタイプに変換することもできます。これは、エラー情報を抽出するのに便利です。これらの関数を使用すると、エラー処理ロジックを簡素化できますが、エラーチェーンの正しい配信に注意を払い、コードの複雑さを防ぐために過度の依存性を回避できます。

tomakegogoapplicationsRunfasterAndMore -efficient、useprofilingtools、leverageconconcurrency、andmanagememoryefcectively.1)useprofforcpuandmemoryprofilingtoidentififybottlenecks.2)


ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

Video Face Swap
完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 Linux 新バージョン
SublimeText3 Linux 最新バージョン

VSCode Windows 64 ビットのダウンロード
Microsoft によって発売された無料で強力な IDE エディター

SAP NetWeaver Server Adapter for Eclipse
Eclipse を SAP NetWeaver アプリケーション サーバーと統合します。

mPDF
mPDF は、UTF-8 でエンコードされた HTML から PDF ファイルを生成できる PHP ライブラリです。オリジナルの作者である Ian Back は、Web サイトから「オンザフライ」で PDF ファイルを出力し、さまざまな言語を処理するために mPDF を作成しました。 HTML2FPDF などのオリジナルのスクリプトよりも遅く、Unicode フォントを使用すると生成されるファイルが大きくなりますが、CSS スタイルなどをサポートし、多くの機能強化が施されています。 RTL (アラビア語とヘブライ語) や CJK (中国語、日本語、韓国語) を含むほぼすべての言語をサポートします。ネストされたブロックレベル要素 (P、DIV など) をサポートします。
