为了账号安全,请及时绑定邮箱和手机立即绑定

Go 中的 Goroutine 和通道

Go 中的 Goroutine 和通道

Go
aluckdog 2021-11-08 15:55:35
我试图理解一个代码示例,它代表 Go 中的多个读者和作者。此代码示例用于计算一个/多个网页的大小。代码版本 1:package mainimport (    "fmt"    "io/ioutil"    "net/http")func main() {    urls := []string{"http://google.com", "http://yahoo.com", "http://reddit.com"}    sizeCh := make(chan string)    urlCh := make(chan string)    for i := 0; i < 3; i++ { //later we change i<3 to i<2        go worker(urlCh, sizeCh, i)    }    for _, u := range urls {        urlCh <- u //later: go generator(u, urlCh)    }    for i := 0; i < len(urls); i++ {        fmt.Println(<-sizeCh)    }}func worker(urlCh chan string, sizeCh chan string, id int) {    for {        url := <-urlCh        length, err := getPage(url)        if err == nil {            sizeCh <- fmt.Sprintf("%s has legth %d. worker %d", url, length, id)        } else {            sizeCh <- fmt.Sprintf("Error getting %s: %s. worker %d", url, err, id)        }    }}func getPage(url string) (int, error) {    resp, err := http.Get(url)    if err != nil {        return 0, err    }    defer resp.Body.Close()    body, err := ioutil.ReadAll(resp.Body)    if err != nil {        return 0, err    }    return len(body), nil}结果:http://reddit.com has legth 110937. worker 0http://google.com has legth 18719. worker 2http://yahoo.com has legth 326987. worker 1但是在将for i := 0; i < 3; i++(第 15 行)更改为for i := 0; i < 2; i++i < len(urls) 之后,我们没有得到任何结果(总是在等待...)在[版本 2] 中,我们在版本 1 中添加了一个辅助函数:func generator(url string, urlCh chan string) {    urlCh <- url}并将第 19-21 行更改为:for _, u := range urls {    go generator(u, urlCh)}即使使用i<2:http://google.com has legth 18701. worker 1http://reddit.com has legth 112469. worker 0http://yahoo.com has legth 325752. worker 1为什么版本 1 在条件i<2(即i<len(urls))下失败而版本 2 没有?
查看完整描述

1 回答

?
江户川乱折腾

TA贡献1851条经验 获得超5个赞

在您的程序中,您有以下循环遍历 3 个 URL:


for _, u := range urls {

         urlCh <- u //later: go generator(u, urlCh)

}

由于 urlCh 是无缓冲的,循环体中的发送操作将不会完成,直到另一个 Goroutine 执行相应的接收操作。


当你有 3 个工作程序 goroutine 时,这没问题。当您将其减少到两个时,这意味着至少有一个 goroutine 需要进展到足够远才能从urlCh.


现在,如果我们查看主体,worker我们可以看到问题:


for {

    url := <-urlCh

    length, err := getPage(url)

    if err == nil {

        sizeCh <- fmt.Sprintf("%s has legth %d. worker %d", url, length, id)

    } else {

        sizeCh <- fmt.Sprintf("Error getting %s: %s. worker %d", url, err, id)

    }

}

在成功发送 上的值之前,此循环无法完成sizeCh。由于这个通道也是无缓冲的,所以在另一个 goroutine 准备好从该通道接收值之前不会发生这种情况。


不幸的是,唯一会这样做的 goroutine 是main,它只有在完成将值发送到urlCh. 这样我们就陷入了僵局。


移动发送到urlCh分离够程修复了问题,因为main可以进步,它从读取点sizeCh,即使不是所有的价值已被送往urlCh。


查看完整回答
反对 回复 2021-11-08
  • 1 回答
  • 0 关注
  • 175 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信