通过向下通道 Go 传递空结构来停止循环
Stop for loop by passing empty struct down channel Go
我正在尝试在 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()
似乎问题出在您的用例中,您以阻塞方式调用了 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
发送到完成频道。像 close(p.done)
这样关闭频道更 idiomatic for go.
Go 中没有明确的方法来广播事件以执行取消之类的例程。相反,它惯用地创建一个通道,当关闭时表示一条消息,例如取消它必须做的任何工作。类似这样的模式是可行的:
var done = make(chan struct{})
func cancelled() bool {
select {
case <-done:
return true
default:
return false
}
}
Go 例程可以调用取消以轮询取消。
然后你的主循环可以响应这样的事件,但要确保你排空任何可能导致 go-routines 阻塞的通道。
for {
select {
case <-done:
// Drain whatever channels you need to.
for range someChannel { }
return
//.. Other cases
}
}
我正在尝试在 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()
似乎问题出在您的用例中,您以阻塞方式调用了 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
发送到完成频道。像 close(p.done)
这样关闭频道更 idiomatic for go.
Go 中没有明确的方法来广播事件以执行取消之类的例程。相反,它惯用地创建一个通道,当关闭时表示一条消息,例如取消它必须做的任何工作。类似这样的模式是可行的:
var done = make(chan struct{})
func cancelled() bool {
select {
case <-done:
return true
default:
return false
}
}
Go 例程可以调用取消以轮询取消。
然后你的主循环可以响应这样的事件,但要确保你排空任何可能导致 go-routines 阻塞的通道。
for {
select {
case <-done:
// Drain whatever channels you need to.
for range someChannel { }
return
//.. Other cases
}
}