duanci19881003 2017-01-03 16:24
浏览 110
已采纳

使用goroutine,通道和同步时,golang中的递归会给死锁或负的WaitGroup计数器。

I am trying to find the list of all directories using a recursive function. The code to the function is

func FindDirs(dir string, nativePartitions []int64, wg *sync.WaitGroup, dirlistchan chan string) {
    // defer wg.Done here will give negative waitgroup panic, commenting it will give negative waitgroup counter panic
    fd, err := os.Open(dir)
    if err != nil {
        panic(err)
    }
    filenames, err := fd.Readdir(0)
    if err != nil {
        panic(err)
    }

    for _, i := range filenames {

        var buff bytes.Buffer
        buff.WriteString(dir)
        switch dir {
        case "/":
        default:
            buff.WriteString("/")
        }

        buff.WriteString(i.Name())
        /*err := os.Chdir(dir)
        if err != nil {
            return err
        }*/

        t := new(syscall.Statfs_t)
        err = syscall.Statfs(buff.String(), t)
        if err != nil {
            //fmt.Println("Error accessing", buff.String())
        }
        if checkDirIsNative(t.Type, nativePartitions) && i.IsDir(){
            dirlistchan <- buff.String()
            FindDirs(buff.String(), nativePartitions, wg, dirlistchan) //recursion happens here
        } else {
            //fmt.Println(i.Name(), "is not native")
        }


    }
}

and in the main function, I am calling it as

wg := new(sync.WaitGroup)
dirlistchan := make(chan string, 1000)
wg.Add(1)
go func() {
    filtermounts.FindDirs(parsedConfig.ScanFrom, []int64{filtermounts.EXT4_SUPER_MAGIC}, wg, dirlistchan)
}()


go func() {
    wg.Wait()
    close(dirlistchan)
}()
for i := range dirlistchan {
    fmt.Println(i)
}
wg.Wait()

and I am getting a

fatal error: all goroutines are asleep - deadlock!

I was able to get this working if I am printing the result instead of using channels, or append to a slice using mutex. (verified with the linux find command to see if the results are same.) Please find the function after omitting channels and using sync.Mutex and append.

func FindDirs(dir string, nativePartitions []int64, dirlist *[]string, mutex *sync.Mutex) []string{


    fd, err := os.Open(dir)
    defer fd.Close()
    if err != nil {
        panic(err)
    }
    filenames, err := fd.Readdir(0)
    if err != nil {
        panic(err)
    }

    for _, i := range filenames {
        var buff bytes.Buffer
        buff.WriteString(dir)
        switch dir {
        case "/":
        default:
            buff.WriteString("/")
        }

        buff.WriteString(i.Name())
        /*err := os.Chdir(dir)
        if err != nil {
            return err
        }*/

        t := new(syscall.Statfs_t)
        err = syscall.Statfs(buff.String(), t)
        if err != nil {
            //fmt.Println("Error accessing", buff.String())
        }
        if checkDirIsNative(t.Type, nativePartitions) && i.IsDir(){
            //dirlistchan <- buff.String()
            mutex.Lock()
            *dirlist = append(*dirlist, buff.String())
            mutex.Unlock()
            //fmt.Println(buff.String())

            FindDirs(buff.String(), nativePartitions, dirlist, mutex)
        } else {
            //fmt.Println(i.Name(), "is not native")
        }

    }
    return *dirlist
}

But I cannot think of a way to make this work with channels and goroutines. Any help is greatly appreciated.

Note: Here is a link to the golang playground with the code. I couldn't find a workaround to get the syscall thing to work on the playground either. It works on my system though.

Thanks.

  • 写回答

2条回答 默认 最新

  • dongpu1881 2017-01-04 12:39
    关注

    Short answer : You are not closing the channel.

    Fix : add defer wg.Done() at beginning of the go routine that calls FindDirs

    go func() {
        defer wg.Done()
        filtermounts.FindDirs(parsedConfig.ScanFrom, []int64{filtermounts.EXT4_SUPER_MAGIC}, wg, dirlistchan)
    }()
    

    Why did it happen

    The go routine that is responsponsible for closing the channel waits for wg there is no wg.Done in the code above. So close never happens

    Now the for loop blocks on the channel for close or a value for ever, this cause the error

    fatal error: all goroutines are asleep - deadlock!
    

    So here is your code ,this may be run as

    go run filename.go /path/to/folder
    

    Code

    package main
    
    import (
        "bytes"
        "fmt"
        "os"
        "sync"
        "syscall"
    )
    
    func main() {
    
        wg := new(sync.WaitGroup)
        dirlistchan := make(chan string, 1000)
        wg.Add(1)
        go func() {
            defer wg.Done()
            FindDirs(os.Args[1], []int64{61267}, wg, dirlistchan)
        }()
    
        go func() {
            wg.Wait()
            close(dirlistchan)
        }()
        for i := range dirlistchan {
            fmt.Println(i)
        }
        wg.Wait()
    
    }
    
    func FindDirs(dir string, nativePartitions []int64, wg *sync.WaitGroup, dirlistchan chan string) {
        fd, err := os.Open(dir)
        if err != nil {
            panic(err)
        }
        filenames, err := fd.Readdir(0)
        if err != nil {
            panic(err)
        }
    
        for _, i := range filenames {
    
            var buff bytes.Buffer
            buff.WriteString(dir)
            switch dir {
            case "/":
            default:
                buff.WriteString("/")
            }
    
            buff.WriteString(i.Name())
            /*err := os.Chdir(dir)
              if err != nil {
                  return err
              }*/
    
            t := new(syscall.Statfs_t)
            err = syscall.Statfs(buff.String(), t)
            if err != nil {
                //fmt.Println("Error accessing", buff.String())
            }
            if checkDirIsNative(t.Type, nativePartitions) && i.IsDir() {
                dirlistchan <- buff.String()
                FindDirs(buff.String(), nativePartitions, wg, dirlistchan) //recursion happens here
            } else {
                //fmt.Println(i.Name(), "is not native")
            }
    
        }
    }
    
    func checkDirIsNative(dirtype int64, nativetypes []int64) bool {
        for _, i := range nativetypes {
            if dirtype == i {
                return true
            }
        }
        return false
    }
    

    Find the go.play link here

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

报告相同问题?

悬赏问题

  • ¥15 做个有关计算的小程序
  • ¥15 MPI读取tif文件无法正常给各进程分配路径
  • ¥15 如何用MATLAB实现以下三个公式(有相互嵌套)
  • ¥30 关于#算法#的问题:运用EViews第九版本进行一系列计量经济学的时间数列数据回归分析预测问题 求各位帮我解答一下
  • ¥15 setInterval 页面闪烁,怎么解决
  • ¥15 如何让企业微信机器人实现消息汇总整合
  • ¥50 关于#ui#的问题:做yolov8的ui界面出现的问题
  • ¥15 如何用Python爬取各高校教师公开的教育和工作经历
  • ¥15 TLE9879QXA40 电机驱动
  • ¥20 对于工程问题的非线性数学模型进行线性化