golang中的死锁错误
Deadlock error in golang
最近在看go,迷上了,看起来好有趣!完成本教程后,我想自己构建一些东西:我想列出我音乐库中的所有歌曲。我觉得我可以在这里从 go 的并发中获益。当例行程序沿着目录树向下走时,它将音乐文件(这些文件的路径)推入一个通道,然后由另一个读取 ID3 标签的例程拾取,所以我不必等到每个文件都被找到.
这是我简单而天真的方法:
package main
import (
"fmt"
"os"
"path/filepath"
"strings"
"sync"
)
const searchPath = "/Users/luma/Music/test" // 5GB of music.
func main() {
files := make(chan string)
var wg sync.WaitGroup
wg.Add(2)
go printHashes(files, &wg)
go searchFiles(searchPath, files, &wg)
wg.Wait()
}
func searchFiles(searchPath string, files chan<- string, wg *sync.WaitGroup) {
visit := func(path string, f os.FileInfo, err error) error {
if !f.IsDir() && strings.Contains(".mp4.mp3.flac", filepath.Ext(f.Name())) {
files <- path
}
return err
}
if err := filepath.Walk(searchPath, visit); err != nil {
fmt.Println(err)
}
wg.Done()
}
func printHashes(files <-chan string, wg *sync.WaitGroup) {
for range files {
fmt.Println(<-files)
}
wg.Done()
}
这个程序还没有读取标签。相反,它只是打印文件路径。这有效,它以极快的速度列出所有音乐文件!但是我在程序完成后看到这个错误:
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [semacquire]:
sync.runtime_Semacquire(0xc42007205c)
/usr/local/Cellar/go/1.7.4_2/libexec/src/runtime/sema.go:47 +0x30
sync.(*WaitGroup).Wait(0xc420072050)
/usr/local/Cellar/go/1.7.4_2/libexec/src/sync/waitgroup.go:131 +0x97
main.main()
/Users/luma/Code/Go/src/github.com/LuMa/test/main.go:22 +0xfa
goroutine 17 [chan receive]:
main.printHashes(0xc42008e000, 0xc420072050)
/Users/luma/Code/Go/src/github.com/LuMa/test/main.go:42 +0xb4
created by main.main
/Users/luma/Code/Go/src/github.com/LuMa/test/main.go:19 +0xab
exit status 2
是什么导致了死锁?
因为您需要关闭 files
频道。
在你的情况下,你没有关闭它,所以
for range files {
fmt.Println(<-files)
}
将等待从 files
通道获取值。所以 wg.Done()
永远不会在 printHashes
中完成。
func searchFiles(searchPath string, files chan<- string, wg *sync.WaitGroup) {
visit := func(path string, f os.FileInfo, err error) error {
if !f.IsDir() && strings.Contains(".mp4.mp3.flac", filepath.Ext(f.Name())) {
files <- path
}
return err
}
if err := filepath.Walk(searchPath, visit); err != nil {
fmt.Println(err)
}
wg.Done()
close(files) // close the chanel, because you don't put thing into the channel anymore.
}
在 searchFiles
内,您想 close(files)
发送完毕。此约定称为 sender-closes(接收器永不关闭)。另外,删除对 wg.Done()
的调用,因为您还没有完成...频道上可能还有项目。
close(files)
将向 for range files
发出信号以关闭并退出循环,这将调用您的 wg.Done()
向主函数发出一切已完成的信号。
(未在手机上测试)
package main
import (
"fmt"
"os"
"path/filepath"
"strings"
"sync"
)
const searchPath = "/Users/luma/Music/test" // 5GB of music.
func main() {
files := make(chan string)
var wg sync.WaitGroup
wg.Add(1)
go printHashes(files)
go searchFiles(searchPath, files, &wg)
wg.Wait()
}
func searchFiles(searchPath string, files chan<- string) {
visit := func(path string, f os.FileInfo, err error) error {
if !f.IsDir() && strings.Contains(".mp4.mp3.flac", filepath.Ext(f.Name())) {
files <- path
}
return err
}
if err := filepath.Walk(searchPath, visit); err != nil {
fmt.Println(err)
}
close(files)
}
func printHashes(files <-chan string, wg *sync.WaitGroup) {
defer wg.Done()
for range files {
fmt.Println(<-files)
}
}
请注意,虽然这看起来很快,但使用单个 goroutine 就可以了,并且也可以解除对主 goroutine 的阻塞。但是,如果您尝试在多个 goroutine 中读取 id3 标签的多个文件,您可能不会获得任何优势——它们将在系统调用级别共享相同的文件 i/o 锁。唯一有利的方法是,如果数据处理远远超出文件 i/o 锁定的权重(例如,计算量很大,因为处理速度远快于系统调用锁)。
PS,欢迎来到 Go 社区!
最近在看go,迷上了,看起来好有趣!完成本教程后,我想自己构建一些东西:我想列出我音乐库中的所有歌曲。我觉得我可以在这里从 go 的并发中获益。当例行程序沿着目录树向下走时,它将音乐文件(这些文件的路径)推入一个通道,然后由另一个读取 ID3 标签的例程拾取,所以我不必等到每个文件都被找到.
这是我简单而天真的方法:
package main
import (
"fmt"
"os"
"path/filepath"
"strings"
"sync"
)
const searchPath = "/Users/luma/Music/test" // 5GB of music.
func main() {
files := make(chan string)
var wg sync.WaitGroup
wg.Add(2)
go printHashes(files, &wg)
go searchFiles(searchPath, files, &wg)
wg.Wait()
}
func searchFiles(searchPath string, files chan<- string, wg *sync.WaitGroup) {
visit := func(path string, f os.FileInfo, err error) error {
if !f.IsDir() && strings.Contains(".mp4.mp3.flac", filepath.Ext(f.Name())) {
files <- path
}
return err
}
if err := filepath.Walk(searchPath, visit); err != nil {
fmt.Println(err)
}
wg.Done()
}
func printHashes(files <-chan string, wg *sync.WaitGroup) {
for range files {
fmt.Println(<-files)
}
wg.Done()
}
这个程序还没有读取标签。相反,它只是打印文件路径。这有效,它以极快的速度列出所有音乐文件!但是我在程序完成后看到这个错误:
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [semacquire]:
sync.runtime_Semacquire(0xc42007205c)
/usr/local/Cellar/go/1.7.4_2/libexec/src/runtime/sema.go:47 +0x30
sync.(*WaitGroup).Wait(0xc420072050)
/usr/local/Cellar/go/1.7.4_2/libexec/src/sync/waitgroup.go:131 +0x97
main.main()
/Users/luma/Code/Go/src/github.com/LuMa/test/main.go:22 +0xfa
goroutine 17 [chan receive]:
main.printHashes(0xc42008e000, 0xc420072050)
/Users/luma/Code/Go/src/github.com/LuMa/test/main.go:42 +0xb4
created by main.main
/Users/luma/Code/Go/src/github.com/LuMa/test/main.go:19 +0xab
exit status 2
是什么导致了死锁?
因为您需要关闭 files
频道。
在你的情况下,你没有关闭它,所以
for range files {
fmt.Println(<-files)
}
将等待从 files
通道获取值。所以 wg.Done()
永远不会在 printHashes
中完成。
func searchFiles(searchPath string, files chan<- string, wg *sync.WaitGroup) {
visit := func(path string, f os.FileInfo, err error) error {
if !f.IsDir() && strings.Contains(".mp4.mp3.flac", filepath.Ext(f.Name())) {
files <- path
}
return err
}
if err := filepath.Walk(searchPath, visit); err != nil {
fmt.Println(err)
}
wg.Done()
close(files) // close the chanel, because you don't put thing into the channel anymore.
}
在 searchFiles
内,您想 close(files)
发送完毕。此约定称为 sender-closes(接收器永不关闭)。另外,删除对 wg.Done()
的调用,因为您还没有完成...频道上可能还有项目。
close(files)
将向 for range files
发出信号以关闭并退出循环,这将调用您的 wg.Done()
向主函数发出一切已完成的信号。
(未在手机上测试)
package main
import (
"fmt"
"os"
"path/filepath"
"strings"
"sync"
)
const searchPath = "/Users/luma/Music/test" // 5GB of music.
func main() {
files := make(chan string)
var wg sync.WaitGroup
wg.Add(1)
go printHashes(files)
go searchFiles(searchPath, files, &wg)
wg.Wait()
}
func searchFiles(searchPath string, files chan<- string) {
visit := func(path string, f os.FileInfo, err error) error {
if !f.IsDir() && strings.Contains(".mp4.mp3.flac", filepath.Ext(f.Name())) {
files <- path
}
return err
}
if err := filepath.Walk(searchPath, visit); err != nil {
fmt.Println(err)
}
close(files)
}
func printHashes(files <-chan string, wg *sync.WaitGroup) {
defer wg.Done()
for range files {
fmt.Println(<-files)
}
}
请注意,虽然这看起来很快,但使用单个 goroutine 就可以了,并且也可以解除对主 goroutine 的阻塞。但是,如果您尝试在多个 goroutine 中读取 id3 标签的多个文件,您可能不会获得任何优势——它们将在系统调用级别共享相同的文件 i/o 锁。唯一有利的方法是,如果数据处理远远超出文件 i/o 锁定的权重(例如,计算量很大,因为处理速度远快于系统调用锁)。
PS,欢迎来到 Go 社区!