簡體   English   中英

使用goroutines,channels和sync時,golang中的遞歸會給出死鎖或負WaitGroup計數器.Waitgroup

[英]Recursion in golang is giving deadlock or negative WaitGroup counter when using goroutines, channels and sync.Waitgroup

我試圖使用遞歸函數找到所有目錄的列表。 該函數的代碼是

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


    }
}

在主要功能中,我稱之為

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

我得到了一個

fatal error: all goroutines are asleep - deadlock!

如果我打印結果而不是使用通道,或者使用互斥鎖附加到切片,我能夠正常工作。 (使用linux find命令驗證結果是否相同。)請在省略通道並使用sync.Mutex並附加后找到該功能。

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
}

但我想不出一種方法可以使用渠道和goroutines。 任何幫助是極大的贊賞。

注意: 是帶有代碼的golang游樂場的鏈接。 我找不到一個解決方法來讓系統調用的東西在操場上工作。 它適用於我的系統。

謝謝。

簡短的回答 :你沒有closing頻道。

修復 :在調用FindDirs的go例程的開頭添加defer wg.Done()

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

為什么會這樣

負責關閉頻道的go例程等待wg ,上面的代碼中沒有wg.Done 如此接近從未發生過

現在的通道關閉或永久值在循環塊,這會導致錯誤

fatal error: all goroutines are asleep - deadlock!

所以這是你的代碼,這可以運行為

go run filename.go /path/to/folder

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
}

這里找到go.play鏈接

如前所述,如果您希望主goroutine退出,則應關閉通道。

實現示例:在函數func FindDirs您可以為此函數將要生成的每個遞歸func FindDirs調用創建一個附加通道,並在參數中傳遞該新通道。 然后同時收聽所有這些新頻道並將字符串轉發回參數中獲得的函數通道。 關閉所有新通道后,關閉參數中給出的通道。

換句話說,每個func調用都應該有自己的發送通道。 然后將字符串一直轉發到main函數。

這里描述的動態選擇: 如何收聽N個頻道? (動態選擇語句)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM