channelを使って、goroutineと関数の間で、データをやり取りすることができます。
次の例では、スライスの中身を足し合わせた結果を、channelを使って返却します。
func useChannel(s []int, c chan int) {
var sum int
for _, v := range s {
sum += v
}
c <- sum // sumをchannelに入れて返却
}
func main() {
s := []int{2, 3, 7, 8}
c := make(chan int) // channelはint型を指定(サイズを指定しないunbuffered channel。デフォルトでサイズは0)
go useChannelS(s, c)
x := <-c // 返ってきたchannelを受け取る
fmt.Println(x) // 20
}
goroutineの処理の後に記述した、channelの中身を変数に格納する処理
x := <-c
は、後続の処理をブロッキングするため、sync.WaitGroup.Wait()などは必要ありません。
上記例では、最終的な合計結果(sum)をchannelに渡していますが、
close(channel)と,channelからの送信の際にfor文を用いると、
途中経過を出力することができます。
func useChannel(s []int, c chan int) {
var sum int
for _, v := range s {
sum += v
c <- sum // 毎回sumを受信
}
close(c) // forループから抜けたらcloseすることで、cをrangeで回す際の終点を知らせる
}
func main() {
s := []int{2, 3, 8, 9, 12}
c := make(chan int, len(s)) // 第2引数を指定して、あらかじめ容量を確保
go useChannel(s, c)
for i := range c {
fmt.Println(i) // 受信の都度出力
}
}
もちろん、channelの型はint以外にも対応します。
stringの例です。
func useChannelS(s string, c chan string) {
c <- s + "を受け取りましたので返します。"
}
func main() {
c2 := make(chan string)
go useChannelS("A", c2)
xx := <-c2
fmt.Println(xx) // Aを受け取りましたので返します。
}
channelをinterface型にしてswitch typeを使えば、任意の型を指定することができます。
func interfaceChannel(i interface{}, c chan interface{}) {
var rv interface{}
switch ii := i.(type) {
case string:
rv = ii + "ですね"
case int:
rv = ii + 100
default:
rv = "undefined"
}
c <- rv
}
func main() {
// crypto/randパッケージを使用した乱数生成
seed, _ := crand.Int(crand.Reader, big.NewInt(math.MaxInt64))
rand.Seed(seed.Int64())
rn := rand.Int63() % 2
var i interface{}
if rn == 0 {
i = 13
} else {
i = "あかさたな"
}
c3 := make(chan interface{})
go interfaceChannel(i, c3)
xxx := <-c3
fmt.Println(xxx)
}
channelに関する、実に様々なパターンが記述されています。
hori-ryota.com