Как закончить все процедуры после того, как один сделан - PullRequest
0 голосов
/ 18 мая 2018

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

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

    package optimizer

    import (
        "github.com/tevino/abool"
        "errors"
        "sync"
        "runtime"
        "log"
    )

    type Optimizer struct {
        Handlers      []ofdHandler.Handler
    }

    func Make(handlers []ofdHandler.Handler, maxProcs int) Optimizer {
        runtime.GOMAXPROCS(maxProcs)
        return Optimizer{Handlers: handlers}
    }

    func (o Optimizer) Optimize(params operations.GetV1ReceiptsParams) (*models.Receipt, error) {
        var wg sync.WaitGroup
        wg.Add(len(o.Handlers))

        results := make(chan *models.Receipt)
        isCalculated := abool.NewBool(false)

        for _, handler := range o.Handlers {
            go func(handler ofdHandler.Handler) {
                log.Println("Starting handler: ", handler.GetName())
                defer wg.Done()

                if isCalculated.IsSet() {
                    log.Println("Result is calculated, exiting goroutine...")
                    return
                }

                receipt, err := handler.Handle(params)
                if err != nil {
                    log.Println(err)
                    return
                }

                if isCalculated.IsSet() {
                    log.Println("Result is calculated, exiting goroutine...")
                    return
                }

                log.Println("Writing result to channel...")
                isCalculated.Set()
                results <- receipt
            }(handler)
        }

        log.Println("Waiting...")
        wg.Wait()

        if receipt, ok := <-results; ok {
            return receipt, nil
        }

        return nil, errors.New("couldn't optimize with current list of Handlers")
    }

Ответы [ 2 ]

0 голосов
/ 18 мая 2018

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

Сделать результатБуферизованный канал, длина 1:

results := make(chan *models.Receipt, 1)

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

В конце вместоisCalculated.Set() вы должны использовать SetToIf, чтобы не устанавливать флаг / результат дважды (и, следовательно, останавливаться, так как читатель все еще ждет, пока группа ожидания завершит свою работу, и поэтому вторая программа не сможетзаписать в канал, что означает, что группа ожидания никогда не достигает нуля):

log.Println("Writing result to channel...")
if isCalculated.SetToIf(false, true) {
   results <- receipt
}
0 голосов
/ 18 мая 2018

Лучший способ сделать это с context.В блоге Go .

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