【问题标题】:Golang: Interrupting infinite polling having time.SleepGolang:中断无限轮询有时间.Sleep
【发布时间】:2018-10-23 21:06:53
【问题描述】:

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

    func poll() {

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

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

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

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

【问题讨论】:

  • 您问题的第一部分过于宽泛。有很多方法可以中断轮询或循环。请显示您的实际代码,以便提供正确、适当的答案。对于第二个问题,我不知道您所说的“瞬时值”是什么意思。再次,请提供您的代码,并准确描述您想要什么。
  • 您可以尝试github.com/jwells131313/goethe,它的定时器会定期关闭。您可以选择固定计时器或在计时器作业完成后重新启动计时器

标签: go sleep channel ticker


【解决方案1】:

你不能打断time.Sleep()

如果您需要在等待某事时收听其他“事件”,则应使用频道和select 语句。 selectswitch 类似,但都是指通信操作。

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 上发送一个值。

【讨论】:

  • 这正是我想要的!非常感谢您的详细回答。
  • 如果我有多个设备轮询每个设备 1 秒左右,情况会怎样。例如。 device1 然后 sleep(s) 然后 device2 然后 sleep(s) ..就像那样。我需要为每台设备创建一个频道吗?
  • @Logan 您要轮询 2 个设备,交替?如果是这样,只需引入一个 bool 标志,指示下一个刻度到达时要轮询哪个。当它出现时,轮询设备并翻转标志。
  • 不只是 2 台设备,我说的是 10 台设备。轮询将是无限 for 循环中的 device1、2、3....10,我希望在每次设备轮询后延迟 5 秒(睡眠)。
  • @Logan 如果您有n 设备,您可能有一个索引,告诉您在下一个滴答进入时要轮询哪个设备。轮询该设备后,增加索引,如果它通过则将其归零在最后一个设备的索引之上。如果您想一个接一个地轮询它们,仍然不需要为每个设备设置不同的代码。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-17
  • 2016-06-15
  • 2012-05-10
  • 1970-01-01
  • 2022-10-18
  • 1970-01-01
相关资源
最近更新 更多