処理のロックやデットロックが起きるパターン
デットロックチャネルの読み込みのみ
実行例
package main
import (
"fmt"
"time"
)
// ゴルーチンでチャネルが書き込まれない
func sampleGoRoutineA(channel chan<- string) {
fmt.Printf("【開始】 " + time.Now().Format(time.TimeOnly) + " sampleGoRoutineA \n")
time.Sleep(5 * time.Second)
fmt.Printf("【終了】 " + time.Now().Format(time.TimeOnly) + " sampleGoRoutineA \n")
//channel <- "終了 \n"
}
func main() {
channelA := make(chan string)
go sampleGoRoutineA(channelA)
val := <-channelA
fmt.Print(val)
}
結果
【開始】 21:13:08 sampleGoRoutineA
【終了】 21:13:13 sampleGoRoutineA
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan receive]:
main.main()
/Users/hoge/main.go:53 +0x7f
exit status 2
推測
サブルーチンがなくなり、チャネルに値がない状態で読み込みを行うとデットロックになるかもしれあせん。
以下のように、処理に10秒かかる関数を1つ追加し、ゴルーチンで実行します。
package main
import (
"fmt"
"time"
)
func sampleGoRoutineA(channel chan<- string) {
fmt.Printf("【開始】 " + time.Now().Format(time.TimeOnly) + " sampleGoRoutineA \n")
time.Sleep(5 * time.Second)
fmt.Printf("【終了】 " + time.Now().Format(time.TimeOnly) + " sampleGoRoutineA \n")
//channel <- "終了 \n"
}
func sampleRoutineB() {
fmt.Printf("【開始】 " + time.Now().Format(time.TimeOnly) + " sampleGoRoutineB \n")
time.Sleep(10 * time.Second)
fmt.Printf("【終了】 " + time.Now().Format(time.TimeOnly) + " sampleGoRoutineB \n")
}
func main() {
channelA := make(chan string)
go sampleGoRoutineA(channelA)
go sampleRoutineB()
val := <-channelA
fmt.Println(val)
}
結果
【開始】 21:32:52 sampleGoRoutineB
【開始】 21:32:52 sampleGoRoutineA
【終了】 21:32:57 sampleGoRoutineA
【終了】 21:33:02 sampleGoRoutineB
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan receive]:
main.main()
/Users/hoge/main.go:52 +0x8b
exit status 2
2つのルーチンは正常に実行されました。その後デットロックが発生しています。なので、Goのランタイムがルーチンの数とチャネルの読み込みの関係を判断しているのかもしれません。
チャネルの書き込みロック
チャネルの読み込みをせず、チャネルの書き込みのみ行う場合、デットロックにはなりません。しかし、書き込みロックになります。
package main
import (
"fmt"
"time"
)
func sampleGoRoutineA(channel chan<- string) {
fmt.Printf("【開始】 " + time.Now().Format(time.TimeOnly) + " sampleGoRoutineA \n")
channel <- "終了 \n" // 書き込みで処理をロックする。
fmt.Printf("【終了】 " + time.Now().Format(time.TimeOnly) + " sampleGoRoutineA \n")
}
func sampleRoutineB() {
fmt.Printf("【開始】 " + time.Now().Format(time.TimeOnly) + " sampleRoutineB \n")
time.Sleep(10 * time.Second)
fmt.Printf("【終了】 " + time.Now().Format(time.TimeOnly) + " sampleRoutineB \n")
}
func main() {
channelA := make(chan string)
go sampleGoRoutineA(channelA)
sampleRoutineB()
}
結果
【開始】 21:52:09 sampleRoutineB
【開始】 21:52:09 sampleGoRoutineA
【終了】 21:52:19 sampleRoutineB
書き込みロックの為、”【終了】 21:52:14 sampleGoRoutineA” が表示されませんでした。