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 サイトの他の関連記事を参照してください。

Goでは、init関数はパッケージの初期化に使用されます。 1)init関数は、パッケージの初期化時に自動的に呼び出され、グローバル変数の初期化、接続の設定、構成ファイルの読み込みに適しています。 2)ファイルの順序で実行できる複数のinit関数がある場合があります。 3)それを使用する場合、実行順序、テストの難易度、パフォーマンスへの影響を考慮する必要があります。 4)副作用を減らし、依存関係の注入を使用し、初期化を遅延させることをお勧めします。

go'sselectStatementStreamLinesConcurrentProgrambyMultipLexIngoperations.1)Itallow swaitingonMultipleChanneloperations、実行、exectingThefirstreadyone.2)

コンテキストアンドウェイトグループは、フォーマネングに焦点を合わせており、contextAllowsingSignalingCancellationAndDeadlinesAcrossapiboundariesを採用し、GoroutinesscanSclacefly.2)WaitGroupssynchronizeGoroutines、Allcompletebebroproproproproproproprotinesを保証します

goisbenefineformicroservicesdueToitssimplicity、and androbustconcurrencysupport.1)go'sdesignemphasisisimplicityandeficiency、ityformicroservices.2)itscurrencymodelusinggoroutinesandchanlowsallowseaseaseadlinging handlingy.3)

GolangisidealforBuildingsCalables Systemsduetoitsefficiency andConcurrency、Whilepythonexcelsinquickscriptinganddataanalysisduetoitssimplicityand vastecosystem.golang'ssignencouragesclean、readisinediteNeditinesinedinediseNabletinedinedinedisedisedioncourase

Golangは並行性がCよりも優れていますが、Cは生の速度ではGolangよりも優れています。 1)Golangは、GoroutineとChannelを通じて効率的な並行性を達成します。これは、多数の同時タスクの処理に適しています。 2)Cコンパイラの最適化と標準ライブラリを介して、極端な最適化を必要とするアプリケーションに適したハードウェアに近い高性能を提供します。

Golangを選択する理由には、1)高い並行性パフォーマンス、2)静的タイプシステム、3)ガベージ収集メカニズム、4)豊富な標準ライブラリとエコシステムは、効率的で信頼できるソフトウェアを開発するための理想的な選択肢となります。

Golangは迅速な発展と同時シナリオに適しており、Cは極端なパフォーマンスと低レベルの制御が必要なシナリオに適しています。 1)Golangは、ごみ収集と並行機関のメカニズムを通じてパフォーマンスを向上させ、高配列Webサービス開発に適しています。 2)Cは、手動のメモリ管理とコンパイラの最適化を通じて究極のパフォーマンスを実現し、埋め込みシステム開発に適しています。


ホットAIツール

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

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

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

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

人気の記事

ホットツール

MinGW - Minimalist GNU for Windows
このプロジェクトは osdn.net/projects/mingw に移行中です。引き続きそこでフォローしていただけます。 MinGW: GNU Compiler Collection (GCC) のネイティブ Windows ポートであり、ネイティブ Windows アプリケーションを構築するための自由に配布可能なインポート ライブラリとヘッダー ファイルであり、C99 機能をサポートする MSVC ランタイムの拡張機能が含まれています。すべての MinGW ソフトウェアは 64 ビット Windows プラットフォームで実行できます。

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

Dreamweaver Mac版
ビジュアル Web 開発ツール

PhpStorm Mac バージョン
最新(2018.2.1)のプロフェッショナル向けPHP統合開発ツール

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

ホットトピック









