Go

【Go】ロックとデットロック

処理のロックやデットロックが起きるパターン

デットロックチャネルの読み込みのみ

実行例

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” が表示されませんでした。

コメントを残す

メールアドレスが公開されることはありません。

© DeNnie.Lab All Rights Reserved.