Шаблон для одновременного вызова многих сервисов, каждый возвращает значение и ошибку - PullRequest
1 голос
/ 23 мая 2019

Я все еще испытываю, как наилучшим образом использовать каналы. У меня 5 исходящих вызовов на обслуживание (на ответ ~ 2 минуты), и каждый из них дает мне пару возвращаемых значений. например func serviceCall()(T, error)

Я хочу сделать их параллельными, но я нахожу код очень длинным.

По сути, мне нужно создать 5 каналов, 5 структур для хранения возвращаемого значения.

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

основной пакет

import (
    "fmt"
    "math/rand"
    "time"
    "log"
)

// goal: run multiple functions concurrently and process the results.
func main() {
now := time.Now()
    // method1
    type res1 struct {
        news string
        err  error
    }
    type res2 struct {
        n   int
        err error
    }

    ch1 := make(chan *res1)
    ch2 := make(chan *res2)

    go func() {
        var res res1
        res.news, res.err = news1()
        ch1 <- &res
    }()

    go func() {
        var res res2
        res.n, res.err = news2()
        ch2 <- &res
    }()

    one := <-ch1
    if one.err != nil {
        log.Fatal(one.err)
    }
    fmt.Println("news1: ", one.news)

    two := <-ch2
    if two.err != nil {
        log.Fatal(two.err)
    }
    fmt.Println("news2: ", two.n)

    fmt.Println("time elapsed: ", time.Since(now))

}

// first sleeps 5 seconds and returns random number or error.
func news1() (string, error) {
    time.Sleep(time.Second * 5)
    return "new1 is here.", nil
}

// second sleeps random seconds and returns random number or error.
func news2() (int, error) {
    n := rand.Intn(20)
    time.Sleep(time.Duration(n) * time.Second)

    return n, nil
}

Ответы [ 2 ]

1 голос
/ 24 мая 2019

Нет единого шаблона для этого.Есть разные способы сделать это.Самое простое, вероятно, с группой ожидания, которая вообще не требует каналов.Шаблон для этого будет выглядеть следующим образом:

func dostuff() {
    var result1 int
    var result2 string
    var resultN SomeStruct
    var err1, err2, errN error

    wg := sync.WaitGroup{}

    wg.Add(1)
    go func() {
        defer wg.Done()
        result1, err1 = doStuff1()
    }

    wg.Add(1)
    go func() {
        defer wg.Done()
        result2, err2 = doStuff2()
    }

    // repeat as often as you like

    wg.Add(1)
    go func() {
        defer wg.Done()
        resultN, errN = doStuffN()
    }

    wg.Wait()
    // handle results and errors
}

Наиболее очевидным недостатком этого подхода является то, что у вас нет гибкости, чтобы отменить любые невыполненные операции в случае ошибки.Это может иметь или не иметь значения для вас.

0 голосов
/ 23 мая 2019

Насколько я могу судить, ваше требование о 5 исходящих вызовах не отражено в написанном вами примере кода. Пример кода выглядит так, как будто он будет работать (я не запускал его для проверки), но он не охватывает ваш вариант использования.

Я бы порекомендовал начинать с горутинов меньше Этот сайт и связанная с ним беседа на YouTube помогли мне начать понимать, как управлять программами.

https://talks.golang.org/2012/concurrency.slide#1

Это видео также очень помогло мне, когда я заблудился в вышеприведенном разговоре:

https://www.youtube.com/watch?v=LvgVSSpwND8

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...