チャネルの方向とパイプラインパターン
読み取り専用・書き込み専用チャネルとパイプライン
「チャネルの方向とパイプラインパターン」はCoddyKit上の無料Go Academyレッスンです。 これはレッスン4/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはGo Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Go Academyコースには全4レッスンが含まれています。
方向付きチャネル型
Goでは、型シグネチャでチャネルを送信専用または受信専用に制限できます。これにより意図が明確になり、誤用を防げます:
package main
// send-only: can only send, not receive or close (close is allowed by sender)
func sender(out chan<- int) {
out <- 42
}
// receive-only: can only receive, not send or close
func receiver(in <-chan int) int {
return <-in
}
// bidirectional: can do both
func relay(in <-chan int, out chan<- int) {
out <- <-in
}チャネルの方向変換
双方向チャネルはどちらの方向付き型にも暗黙的に変換できますが、その逆はできません:
package main
import "fmt"
func produce(out chan<- int) { out <- 1; close(out) }
func consume(in <-chan int) { fmt.Println(<-in) }
func main() {
ch := make(chan int) // bidirectional
go produce(ch) // auto-converts to chan<- int
consume(ch) // auto-converts to <-chan int
// chan<- int cannot be converted back to <-chan int
}パイプライン: ステージ1 — ジェネレーター
パイプラインは、各ステージが前のステージの出力チャネルから読み取るようにゴルーチンを連結します。ジェネレーターは最初のステージです:
package main
import "fmt"
func generate(nums ...int) <-chan int {
out := make(chan int)
go func() {
for _, n := range nums {
out <- n
}
close(out)
}()
return out
}
func main() {
ch := generate(2, 3, 4, 5)
for v := range ch { fmt.Println(v) }
}パイプライン: ステージ2 — 変換
中間ステージは1つのチャネルから受信し、変換して、別のチャネルへ送信します:
package main
import "fmt"
func generate(nums ...int) <-chan int {
out := make(chan int)
go func() { for _, n := range nums { out <- n }; close(out) }()
return out
}
func square(in <-chan int) <-chan int {
out := make(chan int)
go func() {
for v := range in { out <- v * v }
close(out)
}()
return out
}
func main() {
for v := range square(generate(2, 3, 4, 5)) {
fmt.Println(v) // 4, 9, 16, 25
}
}パイプライン: 3ステージの完全な例
3つのステージ(生成、変換、フィルタリング)を連結します:
package main
import "fmt"
func gen(nums ...int) <-chan int {
c := make(chan int)
go func() { for _, n := range nums { c <- n }; close(c) }()
return c
}
func double(in <-chan int) <-chan int {
c := make(chan int)
go func() { for v := range in { c <- v * 2 }; close(c) }()
return c
}
func evens(in <-chan int) <-chan int {
c := make(chan int)
go func() { for v := range in { if v%2 == 0 { c <- v } }; close(c) }()
return c
}
func main() {
for v := range evens(double(gen(1,2,3,4,5))) {
fmt.Println(v) // 2, 4, 6, 8, 10
}
}doneチャネルによるパイプラインのキャンセル
doneチャネルを渡すと、キャンセルされたときにパイプラインの各ステージを早期終了させることができます:
package main
import "fmt"
func gen(done <-chan struct{}, nums ...int) <-chan int {
out := make(chan int)
go func() {
defer close(out)
for _, n := range nums {
select {
case out <- n:
case <-done: // cancel signal
return
}
}
}()
return out
}
func main() {
done := make(chan struct{})
ch := gen(done, 1, 2, 3, 4, 5)
fmt.Println(<-ch) // 1
close(done) // cancel remaining work
fmt.Println("pipeline cancelled")
}ファンアウト: 作業の分散
ファンアウトは、1つのチャネルから複数のゴルーチンに作業を分散し、並列に処理します:
package main
import ("fmt"; "sync")
func fanOut(in <-chan int, workers int) []<-chan int {
outs := make([]<-chan int, workers)
for i := 0; i < workers; i++ {
out := make(chan int)
outs[i] = out
go func() {
for v := range in { out <- v * v } // each worker processes
close(out)
}()
}
return outs
}
func main() {
fmt.Println("fan-out distributes work to multiple goroutines")
_ = sync.WaitGroup{}
}ファンイン: チャネルの統合
ファンインは複数のチャネルを1つに統合します:
package main
import ("fmt"; "sync")
func merge(cs ...<-chan int) <-chan int {
out := make(chan int)
var wg sync.WaitGroup
for _, c := range cs {
wg.Add(1)
c := c
go func() { defer wg.Done(); for v := range c { out <- v } }()
}
go func() { wg.Wait(); close(out) }()
return out
}
func gen(nums ...int) <-chan int {
c := make(chan int)
go func() { for _, n := range nums { c <- n }; close(c) }()
return c
}
func main() {
for v := range merge(gen(1,2), gen(3,4), gen(5,6)) {
fmt.Println(v)
}
}パイプラインのベストプラクティス
パイプラインの設計原則:
- 各ステージでは、ゴルーチンが
<-chanから読み取り、chan<-に書き込み、処理が完了したら出力チャネルを閉じます - チャネルを閉じるのはプロデューサーだけです。コンシューマーが閉じてはいけません
- キャンセルに対応するため、
doneチャネル(またはコンテキスト)を渡します - すべての関数シグネチャで方向付きチャネル型を使用します
- ゴルーチンのリークを防ぐため、キャンセル時にチャネルを排出します
実践的なパイプライン: CSV処理
パイプラインはデータ処理で力を発揮します。行の読み取り、解析、フィルタリング、変換を行えます:
package main
import ("fmt"; "strings")
func splitLines(data string) <-chan string {
out := make(chan string)
go func() {
for _, line := range strings.Split(data, "\n") {
if line != "" { out <- line }
}
close(out)
}()
return out
}
func main() {
csv := "alice,30\nbob,25\ncarol,35"
for line := range splitLines(csv) {
parts := strings.Split(line, ",")
fmt.Printf("name=%s age=%s\n", parts[0], parts[1])
}
}確認問題
関数シグネチャで方向付きチャネル型を使用する目的は何ですか?
復習: チャネルの方向とパイプライン
まとめ:
chan<- Tは送信専用、<-chan Tは受信専用です。意図を明示でき、コンパイル時の安全性も確保できます- パイプラインでは、各ゴルーチンが1つのチャネルから読み取り、別のチャネルに書き込む形で連結します
- ファンアウト: 1つの入力チャネルから複数のゴルーチンへ分配します
- ファンイン: 複数のチャネルを1つに統合します
- チャネルは必ず送信側から閉じ、キャンセルには done チャネルを使用します
AI チューターと学ぶ Go — 無料
ブラウザでリアルコードを書いて実行し、24/7 の AI チューターから瞬時にサポートを受け、ウェブまたはアプリで続きから学習できます。
- コース
- 51
- レッスン
- 203
よくある質問
「チャネルの方向とパイプラインパターン」レッスンは無料ですか?
はい。「チャネルの方向とパイプラインパターン」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Go Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Go Academyコースには全4レッスンが含まれています。
「チャネルの方向とパイプラインパターン」で何を学びますか?
読み取り専用・書き込み専用チャネルとパイプライン ブラウザで直接実行するハンズオンコードでGo Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Go Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのGo Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン4/4です。
「チャネルの方向とパイプラインパターン」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このGo Academyレッスンでコードを書いて実行できますか?
はい。すべてのGo Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- ゴルーチンの起動
- バッファなしチャネル
- バッファ付きチャネル
- チャネルの方向とパイプラインパターン