#初めに
Goにはchannelと呼ばれる、並行処理用のキューがあります。
基本的なことは他の記事に譲るとして、
これを使うと簡単に同期的にキューが扱え、ブロッキングもしてくれるので並行同期処理をする場合に(Javaなどに比べ)かなり楽に書けます。
channelの例
// 容量10のchannel作成
c := make(chan int, 10)
// キューに送信
c <- 10
// キューから受信し、値を出力
// キューに値がなければロック
fmt.Println(<-c) //10
実は、channelには送信専用・受信専用の型(?)があります。
Effective Go
https://golang.org/doc/effective_go.html#channels
や記事などにもあまり書かれてないので
記載したいと思います。
送信専用のchannel
(コンパイルエラーになります。)
// 送信専用の容量10のchannel作成
c := make(chan<- int, 10)
// キューに送信
c <- 10
// 送信専用なのでコンパイルエラーになる。
fmt.Println(<-c)
受信専用のchannel
(コンパイルエラーになります。)
// 受信専用の容量10のchannel作成
c := make(<-chan int, 10)
// 受信専用なのでコンパイルエラー
c <- 10
fmt.Println(<-c)
使いどころ
上だけ見ると、コンパイルエラーになるので使いどころあるの?という感じですが。
実は、
「標準のchannel型」 → 「入力 or 出力専用のchannel型」
への代入ができます。
(それ以外はできないようです。)
うまく使うと型レベルで宣言し、コンパイル時にチェックできます。
package main
import (
"fmt"
)
func main() {
c := make(chan int, 10)
c2 := make(chan<- int, 10)
c2 = c
// c2に4を送信する
c2 <- 4
// c2は送信専用なのでコンパイルエラー
// fmt.Println(<-c2)
// cからを受信すると4を受け取れる
fmt.Println(<-c)
}
これだけだと、あまり大したことないですが、以下の様に関数呼び出しで使う場面もでてくるかもしれません。
送信専用のchannelの使用例
go tourの非同期的なfibonacciの例ですが
https://go-tour-jp.appspot.com/#66
このfibonacci関数内では、channelに対して送信しかしておりません。
なので
package main
import (
"fmt"
)
func fibonacci(n int, c chan<- int) {
x, y := 0, 1
for i := 0; i < n; i++ {
c <- x
x, y = y, x+y
}
close(c)
}
func main() {
c := make(chan int, 10)
go fibonacci(cap(c), c)
for i := range c {
fmt.Println(i)
}
}
と書くことが出来ます。
http://play.golang.org/p/4YtIDSDGkS
これでfibonacci関数内で送信しかしないと明示でき、
間違えて受信の処理をしてしまうことを防げます。
受信専用のchannelの使用例
非同期に素数を順番にジェネレートする関数です。
呼び出した側は、受信のみにさせることが出来ます。
package main
import (
"fmt"
)
func getPrimeGen() <-chan int {
var gen = make(chan int)
go func() {
var primes = make([]int, 0)
for k := 2; ; k++ {
check := true
for _, value := range primes {
if k % value == 0 {
check = false
break
}
}
if check {
primes = append(primes, k)
gen <- k
}
}
close(gen)
}()
return gen
}
func main() {
//受け取ったchannelは受信のみ
primeGen := getPrimeGen()
for i := 0; i < 10000-1; i++ {
<-primeGen
}
// 素数の10000番目を出力します。
fmt.Println(<-primeGen)
}
#まとめ
channelの送受信用のchannel型もうまく使っていきましょう。