-1

我正在尝试在 Go 中创建一个轮询器,它会旋转并每 24 小时执行一次函数。

我还希望能够停止轮询,我试图通过拥有一个完成的通道并传递一个空结构来停止 for 循环来做到这一点。

在我的测试中,for 只是无限循环,我似乎无法停止它,我是否错误地使用了 done 通道?股票行情按预期工作。

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{}{}
}

这是执行代码并导致无限循环的测试。

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

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

2 回答 2

2

似乎问题出在您的用例中,您poller.Start()以阻塞方式调用,因此poller.Stop()永远不会被调用。go在项目中调用Start/Run方法中的 goroutine很常见,因此,在 中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到完成通道。关闭 channel like对 goclose(p.done)来说更惯用

于 2020-04-03T15:18:50.870 回答
0

Go 中没有明确的方法来广播事件以执行诸如取消之类的例程。相反,它习惯于创建一个通道,该通道在关闭时表示一条消息,例如取消它必须做的任何工作。像这样的东西是一个可行的模式:

var done = make(chan struct{})

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

Go-routines 可以调用 cancelled 来轮询取消。

然后你的主循环可以响应这样的事件,但确保你排空任何可能导致 go-routines 阻塞的通道。

for {
    select {
    case <-done:
    // Drain whatever channels you need to.
        for range someChannel { }
        return
    //.. Other cases
   }
}
于 2020-04-03T15:40:58.327 回答