Пани c, пытаясь прекратить создавать больше горутин - PullRequest
0 голосов
/ 05 февраля 2020

Я пытаюсь распараллелить вызовы API, чтобы ускорить процесс, но я сталкиваюсь с проблемой, когда мне нужно прекратить раскрутку подпрограмм для вызова API, если я получаю ошибку от одного из вызовов подпрограмм. Поскольку я закрываю канал дважды (один раз в части обработки ошибок и после завершения выполнения), я получаю ошибку panic: close of closed channel. Есть ли элегантный способ справиться с этим без программы для pani c? Мы будем благодарны за любую помощь!

Ниже приведен фрагмент псевдокода.

for i := 0; i < someNumber; i++ {
    go func(num int, q chan<- bool) {
        value, err := callAnAPI()
        if err != nil {
            close(q)//exit from the for-loop
        }
        // process the value here
        wg.Done()
    }(i, quit)
}
close(quit)

Чтобы высмеять мой сценарий, я написал следующую программу. Есть ли способ изящно выйти из for-l oop, как только условие (закомментированное) выполнено?

package main

import (
    "fmt"
    "sync"
)

func receive(q <-chan bool) {
    for {
        select {
        case <-q:
            return
        }
    }
}

func main() {
    quit := make(chan bool)

    var result []int
    wg := &sync.WaitGroup{}
    wg.Add(10)
    for i := 0; i < 10; i++ {
        go func(num int, q chan<- bool) {
            //if num == 5 {
            //  close(q)
            //}
            result = append(result, num)

            wg.Done()
        }(i, quit)
    }
    close(quit)
    receive(quit)

    wg.Wait()

    fmt.Printf("Result: %v", result)
}

1 Ответ

0 голосов
/ 05 февраля 2020

Вы можете использовать пакет context , который определяет тип контекста, который переносит крайние сроки, сигналы отмены и другие значения в области запроса через границы API и между процессами.

package main

import (
    "context"
    "fmt"
    "sync"
)

func main() {
    ctx, cancel := context.WithCancel(context.Background())
    defer cancel() // cancel when we are finished, even without error

    wg := &sync.WaitGroup{}

    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func(num int) {
            defer wg.Done()
            select {
            case <-ctx.Done():
                return // Error occured somewhere, terminate
            default: // avoid blocking
            }

            // your code here
            // res, err := callAnAPI()
            // if err != nil {
            //  cancel()
            //  return
            //}

            if num == 5 {
                cancel()
                return
            }

            fmt.Println(num)

        }(i)
    }

    wg.Wait()

    fmt.Println(ctx.Err())
}

Пример: Go Детская площадка

Вы также можете посмотреть этот ответ для более подробного объяснения.

...