前の投稿では、Crystalをやってみる
http://qiita.com/tabetomo/items/754308a6cd48bcf56585
elixirでspawn, channel
http://qiita.com/tabetomo/items/ef3e822e907ae0a37614
と言って、Crystalとelixir上のspawn(軽量スレッド)で送受信する例をやってみました。このページでは同じことをgolangでやってみます。
以下は、crystalでのプログラム例と実行例。
ch1 = Channel(String).new(1)
ch2 = Channel(String).new(1)
done = Channel(Bool).new()
spawn do
puts ch1.receive
puts ch2.receive
puts ch1.receive
done.send(true) # end
end
spawn do
ch1.send("apple")
ch2.send("orange")
end
ch1.send("grape")
done.receive
$ crystall spawn3.cr
grape
orange
apple
golangでspawn(go func)
あまりにあっけなく同じものがgoでかけてしまいました。crystalで書いた時も思いましたが、あっさりしすぎ。
package main
import "fmt"
func main() {
ch1 := make(chan string, 1)
ch2 := make(chan string, 1)
done := make(chan bool, 0)
go func() {
msg := <-ch1
fmt.Println(msg)
msg = <-ch2
fmt.Println(msg)
msg = <-ch1
fmt.Println(msg)
done <- true
}()
go func() {
ch1 <- "apple"
ch2 <- "orange"
}()
ch1 <- "grape"
<-done
}
実行例
$ go run spawn3.go
grape
orange
apple
備考;上記のプログラムですが、<-done (ここでは、doneチャネルで何かを受け取るまで待つ)で待ちを入れないと、grape を表示した時点でプログラムが終わってしまいます。
goでは、デッドロックするとエラー?
さて、最初crystalではサイズ0のチャネルでやっていました。
ch1 = Channel(String).new
ch2 = Channel(String).new
略
この場合、チャネルを受信するまで送信の先に処理が進みません(逆に言えば、受信されるまで送信側でブロックして待つことができます)。チャネルのサイズを0にしてしまうと、上記プログラムはgrapeを表示してデッドロックします(Ctrl-Cで止めるまで処理が戻ってきません)。
が、前の投稿のコメント欄ではgoでは、エラーになる、とご指摘をいただいていました。やってみようと思います。
package main
import "fmt"
func main() {
ch1 := make(chan string, 0)
ch2 := make(chan string, 0)
略
普通にgoでもサイズ0でstringのチャネルは作成可能でした(ビルドもできて動作もします)。ここまでは問題ないです。が、
実行例
$ go build spawn3.go
$ ./spawn3
grape
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan receive]:
main.main()
/Users/xxx/proj/go/src/github.com/tabetomo/contest/spawn3.go:23 +0x143
goroutine 5 [chan receive]:
main.main.func1(0xc42001a0c0, 0xc42001a120, 0xc42001a180)
/Users/xxx/proj/go/src/github.com/tabetomo/contest/spawn3.go:12 +0x145
created by main.main
/Users/xxx/proj/go/src/github.com/tabetomo/contest/spawn3.go:17 +0xbb
goroutine 6 [chan send]:
main.main.func2(0xc42001a0c0, 0xc42001a120)
/Users/xxx/proj/go/src/github.com/tabetomo/contest/spawn3.go:19 +0x5a
created by main.main
/Users/xxx/proj/go/src/github.com/tabetomo/contest/spawn3.go:21 +0xe7
のようにgoでは、grapeを表示した時点でデッドロックを検知(fatal error: all goroutines are asleep - deadlock!)して、実行時エラーになりました。これが良いのか悪いのかわかりませんが、all goroutines are asleepでは何もできない、ので、終わる方が良いのでしょう。