【问题标题】:Range on Unbuffered chan with go routines throws all goroutines are asleep - deadlock [duplicate]带有go例程的Unbuffered chan上的范围会抛出所有goroutines都睡着了-死锁[重复]
【发布时间】:2022-05-17 16:21:00
【问题描述】:

我编写了一个示例程序来进行文件搜索,它会搜索但最后失败

致命错误:所有 goroutine 都处于休眠状态 - 死锁!

goroutine 1 [chan 接收]: main.main() ~/Learning/golang/GoFileSearch/file_search.go:52 +0x2a7

我检查了许多 Waitgroups 的示例,这部分对我来说似乎没问题,我尝试将它作为参考传递给函数,但这也产生了相同的结果。

我错过了什么?

package main

import (
    "bufio"
    "flag"
    "fmt"
    "io/fs"
    "log"
    "os"
    "strings"
    "sync"
)

func main() {
    searchTerm := flag.String("s", "", "Search string")
    dirname := flag.String("dirname", ".", "Directory for search")
    flag.Parse()

    if len(*searchTerm) == 0 {
        log.Fatal("enter search term")
    }

    dirInfo,err := os.Stat(*dirname)
    if err != nil {
        panic(err)
    }

    if !dirInfo.IsDir() {
        panic("please enter a directory for dirname")
    }

    files, err := os.ReadDir(*dirname)
    ch := make(chan string)
    wg:= &sync.WaitGroup{}
    defer close(ch)

    for _, file := range files {
        if file.IsDir() {
            continue
        }
        wg.Add(1)

        fmt.Println("Searching in "+*dirname + "/" + file.Name())
        //search for the keyword in the file
        go func() {
            defer wg.Done()
            search(*searchTerm, *dirname, file, ch)
        }()
    }


    for name := range ch {
        fmt.Println("Search term found in ->"+name)
    }
    wg.Wait()
    fmt.Println("Done")
}


func search(searchTerm string, 
    dirname string, 
    fileEntry fs.DirEntry, 
    resultChan chan string) {

    filePath := dirname + "/" + fileEntry.Name()
    file, err := os.Open(filePath)
    if err != nil {
        log.Println(err)
        return
    }
    defer file.Close()

    scanner := bufio.NewScanner(file)
    // optionally, resize scanner's capacity for lines over 64K, see next example
    for scanner.Scan() {
        if strings.Contains(scanner.Text(), searchTerm) {
            fmt.Println("Found")
            resultChan <- fileEntry.Name()
            return
        }
    }
}

如果有任何帮助,我将不胜感激

【问题讨论】:

  • ch 永远不会关闭,所以你陷入了循环。
  • 工人完成后关闭ch。这是an answer that shows how to close the channel
  • 将文件循环参数传递给 goroutine。理由:github.com/golang/go/wiki/…。 goroutine 可能直到循环之后才会开始执行,因此搜索总是获取最后一个 files[] 值。
  • 这个建议解决了我的问题,谢谢!!!但是我仍然不确定,为什么 defer close(ch) 没有成功?
  • 如果文件没有searchTerm,它将永远等待for name := range ch。所以结果打印循环也应该在 goroutine 中。

标签: go synchronization


【解决方案1】:

感谢Cerise Limón 分享的链接,问题是defer close(ch) 调用没有关闭频道

我按照here的建议尝试了解决方法

现在我的代码看起来像这样并且工作正常

package main

import (
    "bufio"
    "flag"
    "fmt"
    "io/fs"
    "log"
    "os"
    "strings"
    "sync"
)

func main() {
    searchTerm := flag.String("s", "", "Search string")
    dirname := flag.String("dirname", ".", "Directory for search")
    flag.Parse()

    if len(*searchTerm) == 0 {
        log.Fatal("enter search term")
    }

    dirInfo,err := os.Stat(*dirname)
    if err != nil {
        panic(err)
    }

    if !dirInfo.IsDir() {
        panic("please enter a directory for dirname")
    }

    files, err := os.ReadDir(*dirname)
    ch := make(chan string)
    wg:= &sync.WaitGroup{}
    //defer close(ch)

    for _, file := range files {
        if file.IsDir() {
            continue
        }
        wg.Add(1)

        fmt.Println("Searching in "+*dirname + "/" + file.Name())
        //search for the keyword in the file
        go func() {
            defer wg.Done()
            search(*searchTerm, *dirname, file, ch)
        }()
    }


    // Close channel after goroutines complete.
    go func() {
        wg.Wait()
        close(ch)
    }()

    for name := range ch {
        fmt.Println("Search term found in ->"+name)
    }

    fmt.Println("Done")
}


func search(searchTerm string,
    dirname string,
    fileEntry fs.DirEntry,
    resultChan chan string) {

    filePath := dirname + "/" + fileEntry.Name()
    file, err := os.Open(filePath)
    if err != nil {
        log.Println(err)
        return
    }
    defer file.Close()

    scanner := bufio.NewScanner(file)
    // optionally, resize scanner's capacity for lines over 64K, see next example
    for scanner.Scan() {
        if strings.Contains(scanner.Text(), searchTerm) {
            fmt.Println("Found")
            resultChan <- fileEntry.Name()
            return
        }
    }
}

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-01-06
  • 2020-07-16
  • 2016-04-06
  • 1970-01-01
  • 2023-02-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多