質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Go

Go(golang)は、Googleで開発されたオープンソースのプログラミング言語です。

Q&A

解決済

1回答

2279閲覧

チャネルで理解出来ない挙動があるので解説お願いしたいです。

puroko3

総合スコア185

Go

Go(golang)は、Googleで開発されたオープンソースのプログラミング言語です。

0グッド

0クリップ

投稿2018/07/16 14:06

編集2018/07/17 12:22

Go

1package main 2 3import ( 4 "fmt" 5 "math/rand" 6 "time" 7) 8 9const goroutines = 10 10const maxProcesses = 3 11 12func main() { 13 //3つまでint型を受け取れるチャネル 14 semaphore := make(chan int, maxProcesses) 15 //10つまでint型を受け取れるチャネル 16 notify := make(chan int, goroutines) 17 18 for i := 0; i < goroutines; i++ { 19 fmt.Println(i, "i") 20 go func(no int, semaphore chan int, notify chan<- int) { 21 //semaphoreに0を送信 22 semaphore <- 0 23 //ランダムな秒数で処理を一時停止させる 24 time.Sleep(time.Duration(rand.Int63n(3)) * time.Second) 25 //semaphoreに送信された値を取り出してバッファを解放する。解放しなかれば3回ループした時点で止まる。 26 fmt.Println(<-semaphore, "semaphore受信") 27 //notifyに引数のnoを送信する 28 notify <- no 29 }(i, semaphore, notify) 30 } 31 for i := 0; i < goroutines; i++ { 32 //notifyに送信された値を全て取り出す。同期の意味合いも持つ 33 fmt.Println(<-notify, "notify受信") 34 } 35 fmt.Println("全て完了") 36}

基礎からわかるGo言語という書籍のサンプルコードを少し変形させて、自分なりの理解をコメントにしました。
もし理解に誤りがあるようなら指摘してくださると嬉しいです。

本題に入りまして、今つまづいているのはsemaphoreの部分です。
このコードを実行させると大体の場合、forのiの0から3以上が表示されて、
その後、semaphoreの受信が表示されるのですが、その前にsemaphore <- 0の部分が3つまでしか受け取れないのでエラーが起きてしまうように感じます。

<- semaphoreでバッファの解放?を行っているみたいですが、先にiが3以上まで表示されるという事は、解放する前に受け取っているという事なので、エラーが起きないのが何故なのか理解出来てない状況です...

わかるかたいらしたらお願いします

fmt.Println(<-semaphore, "semaphore受信")
の部分をコメントアウトした時のエラーメッセージ
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan receive]:
main.main()
C:/Go/src/mytest/test1.go:33 +0x18b
goroutine 5 [chan send]:
main.main.func1(0x0, 0xc04206a080, 0xc042076000)
C:/Go/src/mytest/test1.go:22 +0x3e
created by main.main
C:/Go/src/mytest/test1.go:20 +0x14b

goroutine 6 [chan send]:
main.main.func1(0x1, 0xc04206a080, 0xc042076000)
C:/Go/src/mytest/test1.go:22 +0x3e
created by main.main
C:/Go/src/mytest/test1.go:20 +0x14b

goroutine 7 [chan send]:
main.main.func1(0x2, 0xc04206a080, 0xc042076000)
C:/Go/src/mytest/test1.go:22 +0x3e
created by main.main
C:/Go/src/mytest/test1.go:20 +0x14b

goroutine 10 [chan send]:
main.main.func1(0x5, 0xc04206a080, 0xc042076000)
C:/Go/src/mytest/test1.go:22 +0x3e
created by main.main
C:/Go/src/mytest/test1.go:20 +0x14b

goroutine 11 [chan send]:
main.main.func1(0x6, 0xc04206a080, 0xc042076000)
C:/Go/src/mytest/test1.go:22 +0x3e
created by main.main
C:/Go/src/mytest/test1.go:20 +0x14b

goroutine 12 [chan send]:
main.main.func1(0x7, 0xc04206a080, 0xc042076000)
C:/Go/src/mytest/test1.go:22 +0x3e
created by main.main
C:/Go/src/mytest/test1.go:20 +0x14b

goroutine 13 [chan send]:
main.main.func1(0x8, 0xc04206a080, 0xc042076000)
C:/Go/src/mytest/test1.go:22 +0x3e
created by main.main
C:/Go/src/mytest/test1.go:20 +0x14b
exit status 2

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

「semaphore <- 0の部分が3つまでしか受け取れないのでエラーが起きてしまうように感じます。」は誤解です。
semaphoreに0を送信において、semaphoreがcloseでなければエラーが発生することはありません。
空きがあれば送信できるし、空きがなければ空きができるまでブロック(処理待ち)します。

投稿2018/07/17 02:00

nobonobo

総合スコア3367

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

puroko3

2018/07/17 04:33 編集

回答ありがとうございます! すいませんまだ納得出来ていない部分があるので追加質問させていただきたいです。 (コメント欄なのでインデントが崩れています。) package main func main() { ch := make(chan int, 2) ch <- 1 ch <- 2 ch <- 3 } 空きがなければ処理待ちされるとありますが、此方のコードでは3を送信した所でエラーが起きます。 質問に張り付けたコードとこのコードの差は何でしょうか?
aimof

2018/07/17 07:16

このコメントにおける問題点は、 「goroutineが立ち上がっていないのにチャネルに送信しようとしていること」です。 実行すると、 `ch <- 1` の時点でエラーになります。 goroutineが立ち上がっていたない状態でチャネルを利用することはできません。
puroko3

2018/07/17 08:18

コメントありがとうございます! コメントのコードは https://go-tour-jp.appspot.com/concurrency/3 とほぼ同じコードですが、 私の環境だと ch <- 3をなくせば動きます コメントのコードのエラーは以下の通りです。 fatal error: all goroutines are asleep - deadlock! goroutine 1 [chan send]: main.main() C:/Go/src/mytest/test1.go:7 +0x94 exit status 2
aimof

2018/07/17 08:38

失礼しました。 deadlockなので、容量内であれば、goroutineが立ち上がっていなくても入ります。 容量を超えてチャネルに送信しようとしたときに(今回の場合は3個目)、Goroutineが立ち上がっていない場合にエラーになります。
puroko3

2018/07/17 09:41

なるほど、大分理解出来てきました。 下のようなコードなgoroutineの中だと、確かにエラーが起きることはありませんでした。 package main import "fmt" func test1(ch chan int) { ch <- 1 ch <- 2 ch <- 3 } func main() { ch := make(chan int, 2) //test1() だとエラー go test1(ch) fmt.Println(<-ch, <-ch, <-ch) } ただそうなると今度は、質問のコードの fmt.Println(<-semaphore, "semaphore受信")の部分をコメントアウトすると、notify受信のループ部分が4回目で停止してdeadlockエラーが出るのが何故なのかがわからなくなりました。
nobonobo

2018/07/17 11:06

deadlockチェックはよくできていて、goroutineはchanへの送信待ちリスト、chanからの受信待ちリストかそれ以外かに分類されています。deadlock検出時のpanic表示がその理解の手助けになります。 7つのsemaphore送信まちのgoroutineと1つのnotify受信まちのgoroutineに分類され、それ以外に属しているgoroutineがいなくなったため、以降どんなに待っても待ちが解消されることがないということが確定したからdeadlock検出panicになったのです。
nobonobo

2018/07/17 11:17

panicやエラー時のメッセージは回答するのに大変重要なヒントが乗っているので、何か表示されるのならそれを質問に貼るのをお勧めします。
puroko3

2018/07/17 11:34 編集

ありがとうございます! ようやく理解出来ました。 send receiveは互いに相殺するような関係にあり、相殺が不可能になった時点で止まるという事ですね。 エラーの件次から気を付けます。 また機会があればお願いします。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問