2015-09-17 2 views
1

Я пытаюсь понять пример кода, который представляет несколько читателей и писателей в Go.Goroutines and channels in Go

Этот пример кода используется для расчета размера (ов) веб-страницы/веб-страниц.

Код версия 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 
} 

Результат:

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

Но после изменения for i := 0; i < 3; i++ (строка 15) for i := 0; i < 2; i++, namly I < LEN (URL), мы не получим никакого результата (всегда в ожидании ...)

В [версия 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 1 
http://reddit.com has legth 112469. worker 0 
http://yahoo.com has legth 325752. worker 1 

Почему версия 1 сбой при условии i<2 (т.е. i<len(urls)), но версии 2 нет?

+0

Вы должны включить код, необходимый для понимания вашего вопроса в вопросе, а не по внешней ссылке (что затрудняет работу тех, кто хочет помочь и которые могут не работать в будущем и т. Д.). –

ответ

2

В вашей программе, вы следующий цикл итерации по 3 адресам:

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

С urlCh небуферизован, операция отправки в теле цикла будет не полным, пока соответствующая операция приема выполняется другим Goroutine.

Когда у вас было 3 рабочих гортани, это не проблема. Когда вы уменьшаете его до двух, это означает, что по крайней мере один горутин должен пройти достаточно далеко, чтобы получить второе значение от 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. И поскольку этот канал также не буферизирован, этого не произойдет, пока другой горутин не будет готов получать значение от этого канала.

К сожалению, единственный горутин, который будет делать это main, который делает это только после того, как он будет отправлен в urlCh. Таким образом, мы зашли в тупик.

Перемещение посылает urlCh отделить goroutines исправляет эту проблему, потому что main может прогрессировать до точки, где он читает из sizeCh, даже если не все значения были отправлены в urlCh.

Смежные вопросы