INDEX
chanの概要
ゴルーチン間の通信 Goの予約語 | ||
chan 概要 |
||
|
基本的なchanの使い方
チャネルを使って、ゴルーチン間でデータを送受信する基本的な例です。
package main
import (
"fmt"
)
func main() {
ch := make(chan string) // チャネルを作成
go func() {
ch <- "Hello, Channel!" // チャネルにデータを送信
}()
msg := <-ch // チャネルからデータを受信
fmt.Println(msg)
}
解説:
ch := make(chan string)
で、文字列型のチャネルを作成します。- ゴルーチン内で
ch <- "Hello, Channel!"
を実行し、チャネルにデータを送信します。 - メインゴルーチンで
msg := <-ch
を実行し、チャネルからデータを受信します。 - このコードでは、受信するまで送信側がブロックされるため、データの受け渡しが確実に行われます。
実行結果:
Hello, Channel!
バッファなしチャネル(同期チャネル)
バッファなしのチャネルは、送信側と受信側が揃わないと処理が進まないため、同期的にデータをやり取りします。
package main
import (
"fmt"
)
func main() {
ch := make(chan int) // バッファなしチャネル
go func() {
fmt.Println("送信開始")
ch <- 42 // 受信側がいないとブロックされる
fmt.Println("送信完了")
}()
fmt.Println("受信:", <-ch)
}
解説:
make(chan int)
でバッファなしの整数チャネルを作成します。- ゴルーチン内で
ch <- 42
を実行すると、受信側がデータを受け取るまでブロックされます。 - 受信側の
fmt.Println("受信:", <-ch)
が実行されると、送信が完了し、ゴルーチンの処理が続行します。
実行結果:
送信開始 受信: 42 送信完了
バッファ付きチャネル(非同期チャネル)
バッファ付きチャネルは、一定数のデータを送信側が待たずに格納できるため、非同期的にデータをやり取りできます。
package main
import (
"fmt"
)
func main() {
ch := make(chan string, 2) // バッファサイズ2のチャネル
ch <- "メッセージ1"
ch <- "メッセージ2"
fmt.Println(<-ch) // 先に送ったメッセージから取り出す
fmt.Println(<-ch)
}
解説:
make(chan string, 2)
で、バッファサイズ2のチャネルを作成します。- 送信側はバッファにデータを2つまで格納でき、受信側が取り出す前に送信できます。
- 3つ目のデータを送信しようとすると、バッファが空くまでブロックされます。
実行結果:
メッセージ1 メッセージ2
チャネルのクローズとrangeを使った受信
チャネルをクローズすると、データがなくなったことを受信側が検知できます。
package main
import "fmt"
func main() {
ch := make(chan int, 3)
ch <- 10
ch <- 20
ch <- 30
close(ch) // チャネルをクローズ
for val := range ch {
fmt.Println(val) // クローズされるまでデータを取得
}
}
解説:
close(ch)
を使うと、チャネルがクローズされ、新たなデータを送信できなくなります。range ch
を使うと、チャネルがクローズされるまでデータを順番に取り出します。
実行結果:
10 20 30
selectを使った複数のチャネルの待機
select
を使うと、複数のチャネルの送受信を同時に監視できます。
package main
import (
"fmt"
"time"
)
func main() {
ch1 := make(chan string)
ch2 := make(chan string)
go func() {
time.Sleep(2 * time.Second)
ch1 <- "チャネル1のデータ"
}()
go func() {
time.Sleep(1 * time.Second)
ch2 <- "チャネル2のデータ"
}()
select {
case msg := <-ch1:
fmt.Println("受信:", msg)
case msg := <-ch2:
fmt.Println("受信:", msg)
}
}
解説:
select
を使うと、どのチャネルが最初にデータを送るかを待ち、受信可能なものから処理を実行します。
実行結果:
受信: チャネル2のデータ
注意事項
- バッファなしチャネルは送受信が同期: 送信側と受信側が揃わないと進まない。
- バッファ付きチャネルは非同期: バッファが空くまでデータを送信できる。
- クローズしたチャネルへの送信はエラー:
close()
後に送信するとパニックが発生。
よくある質問
- Q: チャネルをクローズする必要はありますか?
- A: 受信側がデータの終了を知る必要がある場合にクローズします。ただし、送信側が存在しなくなる場合は、明示的にクローズしなくてもガベージコレクションの対象になります。
- Q: チャネルを二重にクローズするとどうなりますか?
- A: ランタイムエラー(panic)が発生します。
- Q: チャネルがクローズされたかどうかを確認する方法は?
- A: 受信時に2つ目の戻り値を使います。
val, ok := <-ch
でok == false
ならチャネルがクローズされています。 - Q: バッファサイズを大きくするとどんなメリットがありますか?
- A: 送信側がブロックされるのを防ぐことができますが、過度に大きくするとメモリを圧迫する可能性があります。
- Q: selectとチャネルを組み合わせるメリットは?
- A: 複数のチャネルを同時に待機し、最初に受信可能になったものを処理できるため、効率的な非同期処理が可能になります。
まとめ
chan
はゴルーチン間のデータ通信を行うための機能。- バッファなしチャネルは同期的、バッファ付きチャネルは非同期的にデータを送受信できる。
- クローズされたチャネルを
range
で処理すると、安全にデータを取り出せる。 select
を使うと、複数のチャネルの受信を待機できる。