Golang:中断 time.Sleep 的无限轮询

Golang: Interrupting infinite polling having time.Sleep

我正在使用以下简单的轮询机制:

    func poll() {

        for {
            if a {
                device1()
                time.Sleep(time.Second * 10)
            } else {
                sensor1()
                time.Sleep(time.Second * 10)
            }
        }
    }

仅当 "a" 为真时我才需要轮询设备 1,否则轮询传感器 1。现在这里 "a" 将通过单击 UI 上的按钮设置为真,这将是一个随机行为。

但是由于 time.Sleep,在检查条件时引入了延迟。

有没有办法在我得到"a"的值时立即停止time.Sleep? 在 golang 中轮询时有哪些可能的方式来实现此类中断?

你不能打断time.Sleep()

相反,如果您需要在等待时收听其他 "events",您应该使用频道和 select 语句。 select类似于switch,但都是指通信操作。

select的好处是你可以列出多个案例,都是指comm。操作(例如通道接收或发送),并且将选择可以进行的操作()。如果 none 可以继续,它将阻塞(等待)直到通信之一。操作可以继续(除非有 default)。

一个"timeout"事件可以使用time.After(), or a series of "continuous" timeout events (ticks or heartbeats) using time.Ticker实现。

您应该更改按钮处理程序以在通道上发送一个值(按钮状态),因此可以将来自该通道的接收添加到 poll() 内的 select,并处理 "immediately"(无需等待超时或下一个滴答事件)。

看这个例子:

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

此解决方案可以轻松添加任意数量的事件源,而无需更改任何内容。例如,如果你想在上面的解决方案中添加一个 "shutdown" 事件和一个 "read-now" 事件,它看起来像这样:

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 "monitoring" 它,所有的 goroutines 都会收到关闭消息(这就像广播)。从关闭的通道接收可以立即进行,产生通道元素类型的零值。

要发出 "immediate read" 操作,您将在 readNowCh 上发送一个值。