你不能打断time.Sleep()。
如果您需要在等待某事时收听其他“事件”,则应使用频道和select 语句。 select 与switch 类似,但都是指通信操作。
select的好处是可以列出多个case,都引用comm。操作(例如通道接收或发送),并且将选择可以进行的任何操作(choosing one randomly if multiple can proceed)。如果没有一个可以继续,它将阻塞(等待)直到其中一个通信。操作可以继续(除非有 default)。
可以使用time.After() 实现“超时”事件,或使用time.Ticker 实现一系列“连续”超时事件(滴答声或心跳)。
您应该更改您的按钮处理程序以在通道上发送一个值(按钮状态),因此从该通道接收可以添加到 poll() 内的 select,并“立即”处理(无需等待超时或下一个滴答事件)。
看这个例子:
func poll(buttonCh <-chan bool) {
isDevice := false
read := func() {
if isDevice {
device1()
} else {
sensor1()
}
}
ticker := time.NewTicker(1 * time.Second)
defer func() { ticker.Stop() }()
for {
select {
case isDevice = <-buttonCh:
case <-ticker.C:
read()
}
}
}
func device1() { log.Println("reading device1") }
func sensor1() { log.Println("reading sensor1") }
测试它:
buttonCh := make(chan bool)
// simulate a click after 2.5 seconds:
go func() {
time.Sleep(2500 * time.Millisecond)
buttonCh <- true
}()
go poll(buttonCh)
time.Sleep(5500 * time.Millisecond)
输出(在Go Playground上试试):
2009/11/10 23:00:01 reading sensor1
2009/11/10 23:00:02 reading sensor1
2009/11/10 23:00:03 reading device1
2009/11/10 23:00:04 reading device1
2009/11/10 23:00:05 reading device1
此解决方案可以轻松添加任意数量的事件源,而无需更改任何内容。例如,如果您想在上述解决方案中添加“关机”事件和“立即阅读”事件,它的外观如下:
for {
select {
case isDevice = <-buttonCh:
case <-ticker.C:
read()
case <-readNowCh:
read()
case <-shutdownCh:
return
}
}
shutdownCh 是一个具有任何元素类型的通道,并且要发出关闭信号,您可以通过关闭它来实现:
var shutdownCh = make(chan struct{})
// Somewhere in your app:
close(shutdownCh)
关闭频道的好处是你可以有任意数量的 goroutines “监控”它,所有的都会收到关闭(这就像广播)。从关闭的通道接收可以立即进行,产生通道元素类型的零值。
要发出“立即读取”操作,您可以在 readNowCh 上发送一个值。