【问题标题】:Goroutines stuck after executionGoroutines 执行后卡住
【发布时间】:2020-09-11 13:29:17
【问题描述】:

我希望有限数量的 goroutine 进行一些计算(func worker(),它进行一些计算并将结果放在通道中)。还有另一个频道,为我的工人提供“工作”。结果,我可以看到所有作业都已正确计算,但在计算执行后卡住了。

package main
import (
    "bufio"
    "fmt"
    "os"
    "net/http"
    "io/ioutil"
    "strings"
    "time"
)


func worker(id int, urls <- chan string, results chan<- int) {
    var data string
    for url := range urls {
        fmt.Println("worker", id, "started  job", url)
        if (strings.HasPrefix(url, "http") ||  strings.HasPrefix(url, "https")) {
            resp, err := http.Get(url)
            if err != nil {
                fmt.Println(err)
            }
            defer  resp.Body.Close()
            body, err := ioutil.ReadAll(resp.Body)
            if err != nil {
                fmt.Println(err)
            }
            data = string(body)
        } else {
            body, err := ioutil.ReadFile(url)
            if err != nil {
                fmt.Println(err)
            }
            data = string(body)
        }
        number := strings.Count(data, "Go")
        fmt.Println("worker", id, "finished  job", url, "Number of Go is", number)
        results <- number
    }
    return
}

func main() {
    final_result := 0
    maxNbConcurrentGoroutines := 5
    numJobs := 0
    urls := make(chan string)
    results := make(chan int)

    scanner := bufio.NewScanner(os.Stdin)
    start := time.Now()
    for w := 1; w <= maxNbConcurrentGoroutines; w++ {
        go worker(w, urls, results)
    }
    for scanner.Scan() {
        url := (scanner.Text())
        urls <- url
        numJobs += 1
    }
    close(urls)
    for num := range results {
        final_result += num
    }
    t := time.Now()
    elapsed := t.Sub(start)
    for i := 1; i <= numJobs; i++ {
        one_result := <- results
        final_result += one_result
    }
    fmt.Println("Number = ", final_result)
    fmt.Println("Time = ", elapsed)
    if err := scanner.Err(); err != nil {
        fmt.Fprintln(os.Stderr, "error:", err)
        os.Exit(1)
    }
}

我尝试使用https://gobyexample.com/worker-pools 从结果通道中提取所有值,但没有成功。我应该怎么做才能把它拆开并走得更远。以下是如何运行它的示例:

echo -e 'https://golang.org\n/etc/passwd\nhttps://golang.org\nhttps://golang.org' | go run 1.go

【问题讨论】:

  • 不相关,你没有处理错误,所以遇到任何错误时都会恐慌。
  • @JimB,你能澄清一下吗,我没明白
  • 打印错误并像什么都没发生一样继续,这不是处理错误。将无效的 url 传递到您的程序中,看看会发生什么。
  • @JimB,是的,我明白了,非常感谢。但我的主要问题是处于堆叠状态。现在,如果作业数大于工人数,它会堆叠。如我所见,它重现了gobyexample.com/worker-pools,但无法正常工作

标签: go concurrency goroutine


【解决方案1】:

您的程序没有返回,因为它等待结果通道的关闭状态。

https://gobyexample.com/worker-pools 中获取结果的循环不同:

for a := 1; a <= numJobs; a++ {
    <-results
}

如果您想使用for num := range results,您需要close(results) 并确定何时调用它。

您可以在https://gobyexample.com/waitgroups查看另一个使用 WaitGroup 的示例

【讨论】:

  • 哦,我明白了,非常感谢。我发现了问题。它位于冗余代码“for num := range results { final_result += num }”
  • 但如果工作数量大于工人数量,它就会卡住。如何处理?
  • 您可以使用缓冲通道作为 url urls := make(chan string, maxNbConcurrentGoroutines) 或使用另一个通道 + goroutine 来解析您的输入
  • 非常感谢,我会试试的。但理论上对我来说不是很清楚(为什么我不能使用简单的频道)
  • 正如您在文档tour.golang.org/concurrency/2 中看到的那样,默认情况下会发送一个接收块,直到对方准备好。缓冲通道仅在缓冲区已满时才会阻塞。
猜你喜欢
  • 2021-06-07
  • 2017-02-10
  • 2015-01-30
  • 1970-01-01
  • 1970-01-01
  • 2015-10-18
  • 1970-01-01
  • 2021-11-02
  • 2017-11-14
相关资源
最近更新 更多