Остановитесь на l oop, передав пустую структуру вниз по каналу Go - PullRequest
0 голосов
/ 03 апреля 2020

Я пытаюсь создать опросщик в Go, который раскручивается и каждые 24 часа выполняет функцию.

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

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

Poller struct {
    HandlerFunc HandlerFunc
    interval    *time.Ticker
    done        chan struct{}
}

func (p *Poller) Start() error {
    for {
        select {
        case <-p.interval.C:
            err := p.HandlerFunc()
            if err != nil {
                return err
            }
        case <-p.done:
            return nil
        }
    }
}

func (p *Poller) Stop() {
    p.done <- struct{}{}
}

Вот тест, выполняющий код и вызывающий бесконечное l oop.

poller := poller.NewPoller(
    testHandlerFunc,
    time.NewTicker(1*time.Millisecond),
)

err := poller.Start()
assert.Error(t, err)
poller.Stop()

Ответы [ 2 ]

2 голосов
/ 03 апреля 2020

Похоже, проблема в вашем случае использования, вы вызываете poller.Start() в режиме блокировки, поэтому poller.Stop() никогда не вызывается. Обычно в go проектах вызывать goroutine внутри Start / Run методов, поэтому в poller.Start() я бы сделал что-то подобное:

func (p *Poller) Start() <-chan error {
    errc := make(chan error, 1 )

    go func() {
        defer close(errc)

        for {
            select {
            case <-p.interval.C:
                err := p.HandlerFunc()
                if err != nil {
                    errc <- err
                    return
                }
            case <-p.done:
                return
            }
        }
    }

    return errc
}

Кроме того, в этом нет необходимости. отправить пустой struct на готовый канал. Закрывающий канал типа close(p.done) больше идиоматический c для go.

0 голосов
/ 03 апреля 2020

В Go нет явного способа передать событие в подпрограммы go для чего-то вроде отмены. Вместо этого его идиоматизм c создает канал, который при закрытии означает сообщение, такое как отмена любой работы, которую он должен сделать. Примерно так это жизнеспособный шаблон:

var done = make(chan struct{})

func cancelled() bool {
    select {
    case <-done:
        return true
    default:
        return false
    }
}     

Go -программы могут вызывать отменено, чтобы опросить отмену.

Тогда ваш основной l oop может ответить на такое событие но убедитесь, что вы удалили все каналы, которые могут вызвать блокировку go -программ.

for {
    select {
    case <-done:
    // Drain whatever channels you need to.
        for range someChannel { }
        return
    //.. Other cases
   }
}
...