TL;DR
内容
ゴルーチンが排他的なリソースを保持したまま他のリソースを要求すると、デッドロックが起こりうる(Coffman conditions)。これは Mutex に限った話ではなく、チャネルでも起こる。
チャネルの容量は、読み込みリソースと書き込みリソースの集まりという排他的なリソースと考えられる。バッファなしチャネルでは、初期状態ではどちらのリソースもない。あるゴルーチンが書き込もうとすると、書き込みリソースを獲得しようとする間に、読み込みリソースを 1 つ利用可能にする。逆に読み込もうとすると、読み込みリソースを獲得しようとする間に、書き込みリソースを 1 つ利用可能にする。そのため、ゴルーチンがあるチャネルを保持しながら、別のチャネルでメッセージを送受信しようとすると、デッドロックが起こりうる。
次の例では、handleDirectories が dirs から受け取ったディレクトリ内のファイルを files に送り、handleFiles が files から受け取ったパスがディレクトリなら dirs に送り返す。互いに相手のチャネルへ送信しようとして待ち続けると、デッドロックになる。
func handleDirectories(dirs <-chan string, files chan<- string) {
for fullpath := range dirs {
fmt.Println("Reading all files from", fullpath)
filesInDir, _ := os.ReadDir(fullpath)
fmt.Printf("Pushing %d files from %s\n", len(filesInDir), fullpath)
for _, file := range filesInDir {
files <- filepath.Join(fullpath, file.Name())
}
}
}
func handleFiles(files <-chan string, dirs chan<- string) {
for path := range files {
file, _ := os.Open(path)
fileInfo, _ := file.Stat()
if fileInfo.IsDir() {
fmt.Printf("Pushing %s directory\n", fileInfo.Name())
dirs <- path
return
}
fmt.Printf("File %s, size: %.2fKB, last modified: %s\n",
fileInfo.Name(), float64(fileInfo.Size())/1024.0,
fileInfo.ModTime().Format(time.ANSIC))
}
}