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 lingo18勾选global solver求解使用的算法
  • ¥15 全部备份安卓app数据包括密码,可以复制到另一手机上运行
  • ¥15 Python3.5 相关代码写作
  • ¥20 测距传感器数据手册i2c
  • ¥15 RPA正常跑,cmd输入cookies跑不出来
  • ¥15 求帮我调试一下freefem代码
  • ¥15 matlab代码解决,怎么运行
  • ¥15 R语言Rstudio突然无法启动
  • ¥15 关于#matlab#的问题:提取2个图像的变量作为另外一个图像像元的移动量,计算新的位置创建新的图像并提取第二个图像的变量到新的图像
  • ¥15 改算法,照着压缩包里边,参考其他代码封装的格式 写到main函数里