はじめに
Goといえば「goroutine と chan」!(goroutineについては以前記事を書いたのでもしご興味あれば参照ください🙇)
でも、チャネルの書き方や動き方って最初は少しわかりづらい。
この記事では、Goのチャネル(chan
)について、初学者向けにまとめてみます。
🎯 この記事でわかること
-
chan
って何?なぜ使うの? - 同期チャネルとバッファ付きチャネルの違い
- 実用的なコード例
- チャネルの注意点と便利な使い方
🧠 チャネル(chan
)とは?
チャネルは、goroutine間でデータを安全にやり取りするための道具です。
Goでは軽量スレッド「goroutine」で並列処理ができますが、データの受け渡しにはチャネルが必要です。
図でイメージすると:
+------------+ chan +------------+
| goroutineA | ------------> | goroutineB |
+------------+ +------------+
🔰 チャネルの基本構文
チャネルの作成
ch := make(chan int)
チャネルに送る(送信)
ch <- 10
チャネルから受け取る(受信)
val := <-ch
fmt.Println(val) // 10
✅ 同期チャネル(バッファなし)
package main
import (
"fmt"
)
func main() {
ch := make(chan int)
go func() {
ch <- 10
}()
val := <-ch
fmt.Println("受信:", val)
}
📦 バッファ付きチャネル
package main
import (
"fmt"
)
func main() {
ch := make(chan int, 2)
ch <- 1
ch <- 2
fmt.Println("受信1:", <-ch)
fmt.Println("受信2:", <-ch)
}
🔁 range でチャネルをループ
package main
import (
"fmt"
)
func main() {
ch := make(chan int)
go func() {
for i := 0; i < 3; i++ {
ch <- i
}
close(ch)
}()
for v := range ch {
fmt.Println("range受信:", v)
}
}
🔍 select で複数チャネルを待つ
package main
import (
"fmt"
)
func main() {
ch1 := make(chan int)
ch2 := make(chan int)
go func() { ch1 <- 1 }()
go func() { ch2 <- 2 }()
select {
case v := <-ch1:
fmt.Println("ch1から受信:", v)
case v := <-ch2:
fmt.Println("ch2から受信:", v)
}
}
⚠️ チャネルの注意点
注意点 | 説明 |
---|---|
送信・受信が揃わないとブロックされる | 特にバッファなしのとき |
チャネルを閉じたあとに送るとpanic | send on closed channel |
クローズは送信側が行う | 受信専用の側が close するのはNG |
🙋♀️ よくある質問
Q. チャネルは使い終わったら close()
すべき?
A. 明示的に range
などで使っているときは close()
が必要です。
ただし、必ずしも毎回 close()
しなければならないわけではありません。