1.すべての例を実行: コードを読むだけではありません。入力して実行し、動作を観察してください。⚠️ このシリーズをどのように進めていきますか?
2.実験と破壊: スリープを削除して何が起こるか確認し、チャネル バッファー サイズを変更し、ゴルーチン数を変更します。
物を壊すことで、その仕組みがわかる
3.動作に関する理由: 変更されたコードを実行する前に、結果を予測してみてください。予期せぬ動作が見られた場合は、立ち止まってその理由を考えてください。解説に挑戦してください。
4.メンタル モデルの構築: 各ビジュアライゼーションはコンセプトを表します。変更されたコード用に独自の図を描いてみてください。
これは、「Mastering Go Concurrency」 シリーズの パート 1 であり、次の内容について説明します。
- ゴルーチンの仕組みとそのライフサイクル
- ゴルーチン間のチャネル通信
- バッファリングされたチャネルとその使用例
- 実践的な例と視覚化
基本から始めて、それらを効果的に使用する方法についての直観を徐々に養っていきます。
少し長くなり、むしろ非常に長くなりますので、準備を整えてください。
私たちはプロセス全体を通して実践的にサポートします。
ゴルーチンの基礎
複数のファイルをダウンロードする簡単なプログラムから始めましょう。
package main import ( "fmt" "time" ) func downloadFile(filename string) { fmt.Printf("Starting download: %s\n", filename) // Simulate file download with sleep time.Sleep(2 * time.Second) fmt.Printf("Finished download: %s\n", filename) } func main() { fmt.Println("Starting downloads...") startTime := time.Now() downloadFile("file1.txt") downloadFile("file2.txt") downloadFile("file3.txt") elapsedTime := time.Since(startTime) fmt.Printf("All downloads completed! Time elapsed: %s\n", elapsedTime) }
次のダウンロードが開始される前に 2 秒間のダウンロードが完了する必要があるため、プログラムには合計 6 秒かかります。これを視覚化してみましょう:
この時間を短くすることができます。go ルーチンを使用するようにプログラムを変更しましょう:
注意: 関数呼び出しの前にキーワードを移動
package main import ( "fmt" "time" ) func downloadFile(filename string) { fmt.Printf("Starting download: %s\n", filename) // Simulate file download with sleep time.Sleep(2 * time.Second) fmt.Printf("Finished download: %s\n", filename) } func main() { fmt.Println("Starting downloads...") // Launch downloads concurrently go downloadFile("file1.txt") go downloadFile("file2.txt") go downloadFile("file3.txt") fmt.Println("All downloads completed!") }
待って何?何も印刷されませんでしたか?なぜ?
何が起こっているのかを理解するために、これを視覚化してみましょう。
上記の視覚化から、ゴルーチンが終了する前に main 関数が存在することがわかります。観察の 1 つは、すべての goroutine のライフサイクルが main 関数に依存しているということです。
注: main 関数自体は goroutine です ;)
これを修正するには、他のゴルーチンが完了するまでメインのゴルーチンを待機させる方法が必要です。これを行うにはいくつかの方法があります:
- 数秒待ちます (裏技)
- WaitGroup の使用 (適切な方法、次)
- チャネルの使用 (これについては以下で説明します)
Go ルーチンが完了するまで 数秒待ちます。
package main import ( "fmt" "time" ) func downloadFile(filename string) { fmt.Printf("Starting download: %s\n", filename) // Simulate file download with sleep time.Sleep(2 * time.Second) fmt.Printf("Finished download: %s\n", filename) } func main() { fmt.Println("Starting downloads...") startTime := time.Now() downloadFile("file1.txt") downloadFile("file2.txt") downloadFile("file3.txt") elapsedTime := time.Since(startTime) fmt.Printf("All downloads completed! Time elapsed: %s\n", elapsedTime) }
これの問題は、ゴルーチンにどれくらいの時間がかかるかわからないことです。場合によっては、それぞれの時間は一定ですが、実際のシナリオでは、ダウンロード時間は変化することがわかっています。
sync.WaitGroup が登場
Go の sync.WaitGroup は、ゴルーチンのコレクションの実行が完了するのを待つために使用される同時実行制御メカニズムです。
ここで、実際の動作を見て視覚化してみましょう:
package main import ( "fmt" "time" ) func downloadFile(filename string) { fmt.Printf("Starting download: %s\n", filename) // Simulate file download with sleep time.Sleep(2 * time.Second) fmt.Printf("Finished download: %s\n", filename) } func main() { fmt.Println("Starting downloads...") // Launch downloads concurrently go downloadFile("file1.txt") go downloadFile("file2.txt") go downloadFile("file3.txt") fmt.Println("All downloads completed!") }
これを視覚化して、sync.WaitGroup の動作を理解しましょう:
カウンターメカニズム:
- WaitGroup は内部カウンターを維持します
- wg.Add(n) はカウンターを n ずつ増やします。
- wg.Done() はカウンターを 1 つデクリメントします
- wg.Wait() はカウンターが 0 に達するまでブロックします。
同期フロー:
- メインの goroutine は goroutine を起動する前に Add(3) を呼び出します
- 各ゴルーチンは完了時に Done() を呼び出します
- メインのゴルーチンは、カウンターが 0 に達するまで Wait() でブロックされます。
- カウンタが 0 に達すると、プログラムは続行され、正常に終了します
避けるべき一般的な落とし穴
package main
import (
"fmt"
"time"
)
func downloadFile(filename string) {
fmt.Printf("Starting download: %s\n", filename)
// Simulate file download with sleep
time.Sleep(2 * time.Second)
fmt.Printf("Finished download: %s\n", filename)
}
func main() {
fmt.Println("Starting downloads...")
startTime := time.Now() // Record start time
go downloadFile("file1.txt")
go downloadFile("file2.txt")
go downloadFile("file3.txt")
// Wait for goroutines to finish
time.Sleep(3 * time.Second)
elapsedTime := time.Since(startTime)
fmt.Printf("All downloads completed! Time elapsed: %s\n", elapsedTime)
}
チャンネル
これで、ゴルーチンがどのように機能するかをよく理解できました。では、2 つの go ルーチンはどのように通信するのでしょうか?ここでチャンネルの出番です。
Go のChannel は、ゴルーチン間の通信に使用される強力な同時実行プリミティブです。これらは、ゴルーチンがデータを安全に共有する方法を提供します。
チャネルをパイプとして考えてください: 1 つのゴルーチンはデータをチャネルに送信でき、別のゴルーチンはそれを受信できます。
ここにいくつかのプロパティがあります:
- チャンネルは本質的にブロックされています。
- チャネルに送信 操作 ch は、他のゴルーチンがチャネルから受信するまで ブロックされます。
- チャネルから受信 操作 ブロックは、他のゴルーチンがチャネルに送信するまで行われます。
package main import ( "fmt" "time" ) func downloadFile(filename string) { fmt.Printf("Starting download: %s\n", filename) // Simulate file download with sleep time.Sleep(2 * time.Second) fmt.Printf("Finished download: %s\n", filename) } func main() { fmt.Println("Starting downloads...") startTime := time.Now() downloadFile("file1.txt") downloadFile("file2.txt") downloadFile("file3.txt") elapsedTime := time.Since(startTime) fmt.Printf("All downloads completed! Time elapsed: %s\n", elapsedTime) }
ch
ゴルーチンを追加してこれを修正しましょう
package main import ( "fmt" "time" ) func downloadFile(filename string) { fmt.Printf("Starting download: %s\n", filename) // Simulate file download with sleep time.Sleep(2 * time.Second) fmt.Printf("Finished download: %s\n", filename) } func main() { fmt.Println("Starting downloads...") // Launch downloads concurrently go downloadFile("file1.txt") go downloadFile("file2.txt") go downloadFile("file3.txt") fmt.Println("All downloads completed!") }
これを視覚化してみましょう:
今回のメッセージは、別の goroutine から送信されているため、チャネルへの送信中にメインはブロックされません。そのため、メッセージは msg :=
チャンネルを使用してメインが他の人を待たない問題を修正
次に、チャネルを使用してファイル ダウンローダーの問題を解決しましょう (メインは他のプログラムが終了するのを待ちません)。
package main import ( "fmt" "time" ) func downloadFile(filename string) { fmt.Printf("Starting download: %s\n", filename) // Simulate file download with sleep time.Sleep(2 * time.Second) fmt.Printf("Finished download: %s\n", filename) } func main() { fmt.Println("Starting downloads...") startTime := time.Now() // Record start time go downloadFile("file1.txt") go downloadFile("file2.txt") go downloadFile("file3.txt") // Wait for goroutines to finish time.Sleep(3 * time.Second) elapsedTime := time.Since(startTime) fmt.Printf("All downloads completed! Time elapsed: %s\n", elapsedTime) }
視覚化:
理解を深めるために予行演習をしてみましょう:
プログラム開始:
メインの goroutine が Done チャネルを作成します
3 つのダウンロードゴルーチンを起動します
各ゴルーチンは同じチャネルへの参照を取得します
ダウンロードの実行:
- 3 つのダウンロードはすべて同時に実行されます
- それぞれに 2 秒かかります
- 任意の順序で終了する可能性があります
チャンネルループ:
- メインのゴルーチンがループに入ります: for i := 0;私は< 3;私
- 各
- ループにより、3 つの完了信号すべてを確実に待機します
ループ動作:
- 反復 1: 最初のダウンロードが完了するまでブロックします
- 反復 2: 2 回目のダウンロードが完了するまでブロックします
- 反復 3: 最終ダウンロードが完了するまでブロックします
完了順序は関係ありません!
所見:
⭐ 各送信 (完了 ⭐ メインのゴルーチンはループを通じてすべてを調整します
2 つのゴルーチンはどのようにして通信できるのでしょうか?
2 つのゴルーチンがどのように通信できるかはすでに見てきました。いつ?この間ずっと。 main 関数も goroutine であることを忘れないでください。
package main import ( "fmt" "time" ) func downloadFile(filename string) { fmt.Printf("Starting download: %s\n", filename) // Simulate file download with sleep time.Sleep(2 * time.Second) fmt.Printf("Finished download: %s\n", filename) } func main() { fmt.Println("Starting downloads...") startTime := time.Now() downloadFile("file1.txt") downloadFile("file2.txt") downloadFile("file3.txt") elapsedTime := time.Since(startTime) fmt.Printf("All downloads completed! Time elapsed: %s\n", elapsedTime) }
これを視覚化して予行演習してみましょう:
ドライラン:
プログラム開始 (t=0ms)
最初のメッセージ (t=1ms)
2 番目のメッセージ (t=101ms)
3 番目のメッセージ (t=201ms)
チャネルクローズ (t=301ms)
完了 (t=302-303ms)
バッファリングされたチャネル
なぜバッファリングされたチャネルが必要なのでしょうか?
バッファなしチャネルは、相手側の準備が整うまで送信側と受信側の両方をブロックします。高頻度の通信が必要な場合、両方のゴルーチンがデータ交換のために一時停止する必要があるため、バッファーのないチャネルがボトルネックになる可能性があります。
バッファリングされたチャネルのプロパティ:
- FIFO (先入れ先出し、キューに似ています)
- 固定サイズ、作成時に設定
- バッファがいっぱいの場合に送信者をブロックします
- バッファが空のときに受信機をブロックします
実際に動作している様子を確認します:
package main import ( "fmt" "time" ) func downloadFile(filename string) { fmt.Printf("Starting download: %s\n", filename) // Simulate file download with sleep time.Sleep(2 * time.Second) fmt.Printf("Finished download: %s\n", filename) } func main() { fmt.Println("Starting downloads...") startTime := time.Now() downloadFile("file1.txt") downloadFile("file2.txt") downloadFile("file3.txt") elapsedTime := time.Since(startTime) fmt.Printf("All downloads completed! Time elapsed: %s\n", elapsedTime) }
出力 (ch
なぜメインのゴルーチンをブロックしなかったのでしょうか?
バッファリングされたチャネルでは、送信者をブロックせずにその容量まで送信できます。
チャネルの容量は 2 です。これは、ブロックする前にバッファーに 2 つの値を保持できることを意味します。
バッファは「first」と「next」ですでにいっぱいです。これらの値を同時に消費する受信者が存在しないため、送信操作は無期限にブロックされます。
メインの goroutine も送信を担当しており、チャネルから値を受信するアクティブな goroutine が他にないため、プログラムは 3 番目のメッセージを送信しようとするとデッドロックに入ります。
3 番目のメッセージのコメントを解除すると、現在容量がいっぱいであるためデッドロックが発生し、バッファが解放されるまで 3 番目のメッセージはブロックされます。
バッファー付きチャネルとバッファーなしチャネルを使用する場合
Aspect | Buffered Channels | Unbuffered Channels |
---|---|---|
Purpose | For decoupling sender and receiver timing. | For immediate synchronization between sender and receiver. |
When to Use | - When the sender can proceed without waiting for receiver. | - When sender and receiver must synchronize directly. |
- When buffering improves performance or throughput. | - When you want to enforce message-handling immediately. | |
Blocking Behavior | Blocks only when buffer is full. | Sender blocks until receiver is ready, and vice versa. |
Performance | Can improve performance by reducing synchronization. | May introduce latency due to synchronization. |
Example Use Cases | - Logging with rate-limited processing. | - Simple signaling between goroutines. |
- Batch processing where messages are queued temporarily. | - Hand-off of data without delay or buffering. | |
Complexity | Requires careful buffer size tuning to avoid overflows. | Simpler to use; no tuning needed. |
Overhead | Higher memory usage due to the buffer. | Lower memory usage; no buffer involved. |
Concurrency Pattern | Asynchronous communication between sender and receiver. | Synchronous communication; tight coupling. |
Error-Prone Scenarios | Deadlocks if buffer size is mismanaged. | Deadlocks if no goroutine is ready to receive or send. |
重要なポイント
: の場合、バッファリングされた
チャネルを使用します- 送信側と受信側のタイミングを切り離す必要があります。
- メッセージをバッチ処理またはキューに入れると、パフォーマンスが向上します。
- アプリケーションは、バッファがいっぱいの場合でもメッセージ処理の遅延を許容できます。
: の場合、バッファなし
チャネルを使用します- ゴルーチン間の同期は重要です。
- シンプルさとデータの即時受け渡しが必要です。
- 送信者と受信者の間の対話は瞬時に行われる必要があります。
これらの基本は、より高度な概念への準備を整えます。今後の投稿では、以下について説明します:
次の投稿:
- 同時実行パターン
- ミューテックスとメモリの同期
Go の強力な同時実行機能について理解を深めていきますので、ご期待ください!
以上が直感的なビジュアルで Golang のゴルーチンとチャネルを理解するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

byteSpackageIngoisESSENTINEFORMANIPULATINGBYTESSLICEFECTILY.1)useBytes.jointoconcatenateSlices.2)employbytes.bufferfordynamicdataConstruction.3)futilizedexandContainsforsearching.4)applaleplaceandtrimodifications.5)usebydificetes.5)

「エンコード/バイナリ」パッケージを包装して、ボディングを作成しているのを補充します

エンコード/バイナリパッケージは、バイナリデータを処理する統一された方法を提供します。 1)binary.writeとbinary.read関数を使用して、整数や浮動小数点番号などのさまざまなデータ型をエンコードおよびデコードします。 2)カスタムタイプは、Binary.byteorderインターフェイスを実装して処理できます。 3)データの正確性と効率性を確保するために、エンディアンネスの選択、データの調整、エラー処理に注意してください。

Goの文字列パッケージは、すべてのユースケースに適していません。最も一般的な文字列操作では機能しますが、複雑なNLPタスク、正規表現マッチング、および特定の形式の解析にはサードパーティライブラリが必要になる場合があります。

Goの文字列パッケージには、多数の文字列操作を処理する際のパフォーマンスとメモリの使用制限があります。 1)パフォーマンスの問題:たとえば、文字列。レプレースと文字列。ReplaceAllは、大規模な文字列置換を扱う場合、効率が低くなります。 2)メモリの使用量:文字列は不変であるため、新しいオブジェクトがすべての操作で生成され、メモリ消費が増加します。 3)Unicode処理:複雑なユニコードルールを処理する場合、柔軟性がなく、他のパッケージやライブラリの助けが必要になる場合があります。

GO言語で文字列パッケージをマスターすると、テキスト処理機能と開発効率が向上します。 1)コンテナ機能を使用してサブストリングを確認し、2)インデックス関数を使用してサブストリング位置を見つけ、3)関数を効率的にスプライスストリングスライス、4)機能を置き換えてサブストリングを置き換えます。空の文字列や大きな文字列操作のパフォーマンスの問題をチェックしないなど、一般的なエラーを避けるように注意してください。

文字列の操作を簡素化し、コードをより明確かつ効率的にすることができるため、GOの文字列パッケージを気にする必要があります。 1)文字列を使用して、弦を効率的にスプライスするために参加します。 2)文字列を使用して、空白の文字で文字列を分割します。 3)文字列を介してサブストリング位置を見つけます。Indexと文字列lastindex; 4)文字列を使用して、文字列を置き換える。 5)文字列を使用して、ビルダーを効率的にスプライスします。 6)予期しない結果を避けるために、常に入力を確認してください。

theStringspackageIngoisESSENTINEFOREFFSTRINGMANIPULATION.1)ITOFFERSSSIMPLEYETPOWERFULFUNCTIONS FORTOSSCHECKINGSUBSTRINGSNINGSTRINGS.2)ITHANDLESUNICODEWELL、ITHANDLESUNICODEWELL


ホットAIツール

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

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

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

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

人気の記事

ホットツール

Safe Exam Browser
Safe Exam Browser は、オンライン試験を安全に受験するための安全なブラウザ環境です。このソフトウェアは、あらゆるコンピュータを安全なワークステーションに変えます。あらゆるユーティリティへのアクセスを制御し、学生が無許可のリソースを使用するのを防ぎます。

WebStorm Mac版
便利なJavaScript開発ツール

ドリームウィーバー CS6
ビジュアル Web 開発ツール

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

MantisBT
Mantis は、製品の欠陥追跡を支援するために設計された、導入が簡単な Web ベースの欠陥追跡ツールです。 PHP、MySQL、Web サーバーが必要です。デモおよびホスティング サービスをチェックしてください。
