dongyong1942 2015-09-17 02:31
浏览 38
已采纳

Go中的Goroutine和频道

I'm trying to understand a code example which represents multiple readers and writers in Go.

This code example is used to calculate the size(s) of a webpage/webpages.

Code version 1:

package main

import (
    "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
}

The result:

http://reddit.com has legth 110937. worker 0
http://google.com has legth 18719. worker 2
http://yahoo.com has legth 326987. worker 1

But after changing for i := 0; i < 3; i++ (line 15) to for i := 0; i < 2; i++, namly i < len(urls), we get no result (always waitting...)

In [version 2], we add a helper function into version 1:

func generator(url string, urlCh chan string) {
    urlCh <- url
}

and change line 19-21 to:

for _, u := range urls {
    go generator(u, urlCh)
}

It works fine even with i<2:

http://google.com has legth 18701. worker 1
http://reddit.com has legth 112469. worker 0
http://yahoo.com has legth 325752. worker 1

Why does the version 1 fail under condition i<2 (i.e.i<len(urls)) but version 2 does not?

  • 写回答

1条回答 默认 最新

  • douzhendi4559 2015-09-17 03:02
    关注

    In your program, you have the following loop iterating over the 3 URLs:

    for _, u := range urls {
             urlCh <- u //later: go generator(u, urlCh)
    }
    

    Since urlCh is unbuffered, the send operation in the loop body will not complete until a corresponding receive operation is performed by another Goroutine.

    When you had 3 worker goroutines, this is no problem. When you reduced it to two, it means that at least one goroutine will need to progress far enough to receive a second value from urlCh.

    Now if we look at the body of worker we can see the problem:

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

    This loop can't complete until it successfully sends a value on sizeCh. And since this channel is also unbuffered, that won't happen until another goroutine is ready to receive a value from that channel.

    Unfortunately the only goroutine that will do that is main, which only does so when it is finished sending values to urlCh. Thus we have a deadlock.

    Moving the sends to urlCh to separate goroutines fixes the problem because main can progress to the point where it is reading from sizeCh, even though not all values have been sent to urlCh.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥25 由IPR导致的DRIVER_POWER_STATE_FAILURE蓝屏
  • ¥50 有数据,怎么建立模型求影响全要素生产率的因素
  • ¥50 有数据,怎么用matlab求全要素生产率
  • ¥15 TI的insta-spin例程
  • ¥15 完成下列问题完成下列问题
  • ¥15 C#算法问题, 不知道怎么处理这个数据的转换
  • ¥15 YoloV5 第三方库的版本对照问题
  • ¥15 请完成下列相关问题!
  • ¥15 drone 推送镜像时候 purge: true 推送完毕后没有删除对应的镜像,手动拷贝到服务器执行结果正确在样才能让指令自动执行成功删除对应镜像,如何解决?
  • ¥15 求daily translation(DT)偏差订正方法的代码