douzhong2954 2017-01-17 00:13
浏览 55
已采纳

Golang中的死锁错误

I recently looked at go and got hooked, it looks so interesting! After completing the tutorial I wanted to build something by myself: I want to list all of my songs from my music library. I think I can profit from go's concurrency here. While on routine is walking down the directory tree it pushes music files (path to those files) into a channel which are then picked up by another routine that reads the ID3 tags, so I don't have to wait until every file has been found.

This is my simple and naive approach:

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()
}

This program doesn't read the tags, yet. Instead it just prints the file path. This works, it lists all music files extremely fast! But I see this error after the program finishes:

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

What is causing the deadlock?

  • 写回答

2条回答 默认 最新

  • dongshukou0240 2017-01-17 00:53
    关注

    Within searchFiles, you want to close(files) when done sending. This convention is called sender-closes (receivers never close). Also, remove the call to wg.Done() as you are not done... There could still be items on the channel.

    The close(files) will signal the for range files to close and exit the loop, which will call your wg.Done() to signal the main function that everything is done.

    (Untested on mobile)

    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)
        }
    }
    

    Note that while this may seem fast, using a single goroutine is fine and unblocks the main goroutine too. But, you may not gain any advantage if you try to read multiple files for id3 tags in multiple goroutines - they will all share the same file i/o lock at the syscall level. The only way that would advantageous would be if the processing of data far out weighs the file i/o locking (e.g. something big in computation, because processing is far faster than syscall locks).

    PS, welcome to the Go community!

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 win2012磁盘空间不足,c盘正常,d盘无法写入
  • ¥15 用土力学知识进行土坡稳定性分析与挡土墙设计
  • ¥70 PlayWright在Java上连接CDP关联本地Chrome启动失败,貌似是Windows端口转发问题
  • ¥15 帮我写一个c++工程
  • ¥30 Eclipse官网打不开,官网首页进不去,显示无法访问此页面,求解决方法
  • ¥15 关于smbclient 库的使用
  • ¥15 微信小程序协议怎么写
  • ¥15 c语言怎么用printf(“\b \b”)与getch()实现黑框里写入与删除?
  • ¥20 怎么用dlib库的算法识别小麦病虫害
  • ¥15 华为ensp模拟器中S5700交换机在配置过程中老是反复重启