2020/02/12

[golang]goroutineをいくつか試す

golangで名前だけ知っていて、試していない機能がいくつもある。

今回はgoroutineをいくつか動かしてみる。


まず、取りあえず動かないこともないコードからいこう。

01: package main
02: 
03: import (
04: 	"fmt"
05: 	"time"
06: )
07: 
08: func gor1() {
09: 	fmt.Printf("gor1\n")
10: }
11: 
12: func main() {
13: 	go gor1()
14: 	time.Sleep(time.Second)
15: }
  

1秒もあればgoroutineの1つくらいは動くよね、というだけで、環境によってはfmt.Printf()が出力されなくてもおかしくはない。

 

関係ないけど、golangは前方参照だっけ?

> There are no forward declarations and no header files; everything is declared exactly once.
https://golang.org/doc/faq#principles

違うそうだ。
まあ、前方参照のつもりで書いていても問題ないだろう。


chanを使うと、その変数は通信チャネルになるらしい。

01: package main
02: 
03: import (
04: 	"fmt"
05: )
06: 
07: func gor1(channel chan int) {
08: 	fmt.Printf("gor1\n")
09: 	channel <- 0
10: }
11: 
12: func main() {
13: 	cn := make(chan int)
14: 	go gor1(cn)
15: 	<-cn
16: }
  

なんかもったいない使い方だが、まあ、普通ならgoroutineにしなくてよい関数をそうしているからだろう。

 

01: package main
02: 
03: import (
04: 	"fmt"
05: 	"time"
06: )
07: 
08: func gor1(channel1 chan int, channel2 chan int) {
09: 	fmt.Printf("gor1-1\n")
10: 	channel1 <- 0
11: 	time.Sleep(time.Second * 3)
12: 	fmt.Printf("gor1-2\n")
13: 	channel2 <- 0
14: }
15: 
16: func main() {
17: 	cn1 := make(chan int)
18: 	cn2 := make(chan int)
19: 	go gor1(cn1, cn2)
20: 	<-cn1
21: 	<-cn2
22: }
  

ふむ、複数chanでもいいのか。

あ、chanの待つ順番をcn2を先にするとどうなるだろう?

$ go run .
gor1-1
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan receive]:
main.main()
        /home/xxx/golang/test/src/ex2/ex2.go:20 +0xa5

goroutine 18 [chan send]:
main.gor1(0xc000062060, 0xc0000620c0)
        /home/xxx/golang/test/src/ex2/ex2.go:10 +0x7d
created by main.main
        /home/xxx/golang/test/src/ex2/ex2.go:19 +0x8e
exit status 2

デッドロックするんだ。
cn1は後でチェックすればいいだけかと思ったが、そういう問題でも無いのか?

Go言語—バッファなしのチャンネルに送受信するとデッドロックが起こる理由 - Qiita
https://qiita.com/YumaInaura/items/943c24ffb64df5e01c65

ほう、cn1がまだ受信の待ち受け状態じゃないのに送りつけたからダメ、ということか。

なので、cn2を1つだけバッファを持たせれば動く。

01: package main
02: 
03: import (
04: 	"fmt"
05: 	"time"
06: )
07: 
08: func gor1(channel1 chan int, channel2 chan int) {
09: 	fmt.Printf("gor1-1\n")
10: 	channel1 <- 0
11: 	time.Sleep(time.Second * 3)
12: 	fmt.Printf("gor1-2\n")
13: 	channel2 <- 0
14: }
15: 
16: func main() {
17: 	cn1 := make(chan int, 1)
18: 	cn2 := make(chan int)
19: 	go gor1(cn1, cn2)
20: 	<-cn2
21: 	<-cn1
22: }
  


chanは双方向のようだ。

01: package main
02: 
03: import (
04: 	"fmt"
05: 	"time"
06: )
07: 
08: func gor1(channel1 chan int, channel2 chan int) {
09: 	fmt.Printf("gor1-1: %d\n", <-channel1)
10: 	time.Sleep(time.Second * 3)
11: 	fmt.Printf("gor1-2\n")
12: 	channel2 <- 2
13: }
14: 
15: func main() {
16: 	cn1 := make(chan int)
17: 	cn2 := make(chan int)
18: 	go gor1(cn1, cn2)
19: 	fmt.Printf("main-1\n")
20: 	cn1 <- 1
21: 	fmt.Printf("main-2: %d\n", <-cn2)
22: }
  

同時に両方書き込むようなタイミングがあるようだったら、mutexみたいな排他処理をするのだろうか。
いろいろ種類がありそうなので、また今度調べよう。

0 件のコメント:

コメントを投稿

コメントありがとうございます。
スパムかもしれない、と私が思ったら、
申し訳ないですが勝手に削除することもあります。