Golang:中断无限轮询有时间。睡着了

时间:2018-05-14 08:50:50

标签: go sleep channel ticker

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

    func poll() {

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

我只需要在“a”为真时轮询device1,否则轮询sensor1。现在,通过单击UI上的按钮将“a”设置为true,这将是一个随机动作。

但由于时间的原因,在检查条件时会引入延迟。

有没有办法立即停止时间。当我得到“a”的值时,是否会睡觉? 在golang中轮询时实现此类中断的可能方法是什么?

1 个答案:

答案 0 :(得分:4)

您无法中断time.Sleep()

相反,如果您在等待某事时需要听其他“事件”,则应使用频道和select语句。 select类似于switch,但案例都涉及通信操作。

select的好处是你可以列出多个案例,所有案例都引用了comm。操作(例如,频道接收或发送),以及可以进行的操作(choosing one randomly if multiple can proceed)。如果没有一个可以继续,它将阻塞(等待)直到其中一个comm。操作可以继续(除非有default)。

使用time.After()或使用time.Ticker的一系列“连续”超时事件(刻度或心跳)可以实现“超时”事件。

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

见这个例子:

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)

关闭频道的优势在于你可以有任意数量的goroutine“监听”它,所有都会收到关机(这就像广播一样)。从封闭通道接收可以立即进行,产生通道元素类型的零值。

要发出“立即阅读”操作,您需要在readNowCh上发送一个值。