Как мне справиться с паникой в ​​горутинах? - PullRequest
0 голосов
/ 06 февраля 2019

Я совсем новичок в golang. Так что, пожалуйста, избавьте меня от меча (если возможно).

Я пытался получить данные из Интернета, изучая учебник здесь

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

Вот мой код игровой площадки .

Прежде чем спросить, я просмотрел множество ссылок, таких как:

  1. Перейти блог отложить, паниковать и восстановить
  2. обработка паники в goroutines
  3. как следует писать-goroutine

И еще несколько, однако я не мог этого понять.

Вот код на тот случай, если вы не хотите идти на игровую площадку (по причинам, пока неизвестнымчеловек):

// MakeRequest : Makes requests concurrently
func MakeRequest(url string, ch chan<- string, wg *sync.WaitGroup) {
    start := time.Now()
    resp, err := http.Get(url)
    defer func() {
        resp.Body.Close()
        wg.Done()
            if r := recover(); r != nil {
                fmt.Println("Recovered in f", r)
            }
    }()
    if err != nil {
        fmt.Println(err)
        panic(err)
    }
    secs := time.Since(start).Seconds()
    body, _ := ioutil.ReadAll(resp.Body)

    ch <- fmt.Sprintf("%.2f elapsed with response length: %d %s", secs, len(body), url)
}

func main() {
    var wg sync.WaitGroup
    output := []string{
        "https://www.facebook.com",
        "",
    }
    start := time.Now()
    ch := make(chan string)
    for _, url := range output {
        wg.Add(1)
        go MakeRequest(url, ch, &wg)
    }

    for range output {
        fmt.Println(<-ch)
    }
    fmt.Printf("%.2fs elapsed\n", time.Since(start).Seconds())
}

Обновление

Я изменил код, чтобы (скажем так) обрабатывать ошибку в goroutine, как это ( go-plays here ):

func MakeRequest(url string, ch chan<- string, wg *sync.WaitGroup) {
    start := time.Now()
    resp, err := http.Get(url)

    if err == nil {
        secs := time.Since(start).Seconds()
        body, _ := ioutil.ReadAll(resp.Body)

        ch <- fmt.Sprintf("%.2f elapsed with response length: %d %s", secs, len(body), url)
        // fmt.Println(err)
        // panic(err)
    }
    defer wg.Done()
}

Обновление 2:

После ответа я изменил код на этот, и он успешно удаляет тупик chan, однако теперь мне нужно с этим справитьсяв main:

func MakeRequest(url string, ch chan<- string, wg *sync.WaitGroup) {
    defer wg.Done()
    start := time.Now()
    resp, err := http.Get(url)

    if err == nil {
        secs := time.Since(start).Seconds()
        body, _ := ioutil.ReadAll(resp.Body)

        ch <- fmt.Sprintf("%.2f elapsed with response length: %d %s", secs, len(body), url)
        // fmt.Println(err)
        // panic(err)
    }
    // defer resp.Body.Close()
    ch <- fmt.Sprintf("")
}

Нет ли более элегантного способа справиться с этим?

Но теперь я зашел в тупик.

Спасибо и всего наилучшего.
Temporarya
(Новичок Голанга)

1 Ответ

0 голосов
/ 06 февраля 2019

Вы используете восстановление правильно.У вас две проблемы:

  1. Вы неправильно используете панику.Вы должны паниковать только тогда, когда произошла ошибка программирования.Избегайте использования паники, если вы не считаете, что отказ от программы - разумный ответ на то, что произошло.В этом случае я бы просто вернул ошибку, а не панику.

  2. Вы паникуете во время вашей паники.То, что происходит, это то, что вы впервые паникуете на panic(err).Затем в своей функции отсрочки вы паникуете на resp.Body.Close().Когда http.Get возвращает ошибку, он возвращает нулевой ответ.Это означает, что resp.Body.Close() действует на нулевое значение.

Идиоматический способ справиться с этим будет выглядеть примерно так:

func MakeRequest(url string, ch chan<- string, wg *sync.WaitGroup) {
    defer wg.Done()
    start := time.Now()
    resp, err := http.Get(url)
    if err != nil {
        //handle error without panicing
    }
    // there was no error, so resp.Body is guaranteed to exist.
    defer resp.Body.Close()
    ...

Ответ на обновление: Если http.Get() вернет ошибку, вы никогда не отправите на канал.В какой-то момент все программы, кроме основной, перестают работать, и основная программа ожидает на <-ch.Так как прием этого канала никогда не завершится и для графика выполнения Go больше ничего не запланировано, он паникует (безвозвратно).


Ответ на комментарий: Чтобы убедиться, что канал не 'Чтобы зависнуть, вам нужна какая-то координация, чтобы знать, когда сообщения перестанут поступать.То, как это реализовано, зависит от вашей реальной программы, и пример не обязательно экстраполирует на реальность.В этом примере я просто закрою канал, когда будет создана группа WaitGroup.

Playground

func main() {
    var wg sync.WaitGroup
    output := []string{
        "https://www.facebook.com",
        "",
    }
    start := time.Now()
    ch := make(chan string)
    for _, url := range output {
        wg.Add(1)
        go MakeRequest(url, ch, &wg)
    }

    go func() {
        wg.Wait()
        close(ch)
    }()

    for val := range ch {
        fmt.Println(val)
    }
    fmt.Printf("%.2fs elapsed\n", time.Since(start).Seconds())
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...