doutan1637 2018-11-09 02:32
浏览 70
已采纳

当选择组中的任何通道在指定时间内没有收到信号时,中断跳出环路

How do I break out of the idiomatic Go for loop containing select statement, if and only if I receive no signals on any of the channels my select statement is listening to, for a particular period of time.

Let me enhance the question with an example.

Setup:

  1. Lets say that i have a channel var listenCh <-chan string that I am listening to.
  2. Let us assume that some other go routine(not in our control) sends different strings on this channel.
  3. I do some processing with the given string and then listen for the next string on the listenCh.

Requirement:

I want to wait 10 seconds maximum(precision not critical), between two successive signals on the listenCh, before I shut down my operations(break out of for loop permanently).

Code Stub:

func doingSomething(listenCh <-chan string) {
  var mystr string
  for {
    select {
      case mystr <-listenCh:
        //dosomething
      case /*more than 10 seconds since last signal on listenCh*/:
        return
    }
  }
}

How would I achieve my requirement in the most efficient manner possible.

The usual quit channel technique with time.After(time.Duration) seems to not reset after one loop and hence the whole program closes in 10 seconds even if there is a continuous stream of values.

I find variants of the question(but not what I want) on SO, but none that I saw answers my particular use case.

  • 写回答

1条回答 默认 最新

  • douyan0732 2018-11-09 02:47
    关注

    Foreword: Using time.Timer is the recommended way, use of time.After() here is only for demonstration and reasoning. Please use the 2nd approach.


    Using time.After() (not recommended for this)

    If you put time.After() in the case branch, that will "reset" in each iteration, because that will return you a new channel each time, so that works:

    func doingSomething(listenCh <-chan string) {
        for {
            select {
            case mystr := <-listenCh:
                log.Println("Received", mystr)
            case <-time.After(1 * time.Second):
                log.Println("Timeout")
                return
            }
        }
    }
    

    (I used 1 second timeout for testability on the Go Playground.)

    We can test it like:

    ch := make(chan string)
    go func() {
        for i := 0; i < 3; i++ {
            ch <- fmt.Sprint(i)
            time.Sleep(500 * time.Millisecond)
        }
    }()
    doingSomething(ch)
    

    Output (try it on the Go Playground):

    2009/11/10 23:00:00 Received 0
    2009/11/10 23:00:00 Received 1
    2009/11/10 23:00:01 Received 2
    2009/11/10 23:00:02 Timeout
    

    Using time.Timer (recommended solution)

    If there is a high rate receiving from the channel, this might be a bit of wasting resources, as a new timer is created and used under the hood by time.After(), which doesn't magically stop and get garbage collected immediately when it's not needed anymore in case you receive a value from the channel before timeout.

    A more resource-friendly solution would be to create a time.Timer before the loop, and reset it if a value is received before a timeout.

    This is how it would look like:

    func doingSomething(listenCh <-chan string) {
        d := 1 * time.Second
        t := time.NewTimer(d)
        for {
            select {
            case mystr := <-listenCh:
                log.Println("Received", mystr)
                if !t.Stop() {
                    <-t.C
                }
                t.Reset(d)
            case <-t.C:
                log.Println("Timeout")
                return
            }
        }
    }
    

    Testing and output is the same. Try this one on the Go Playground.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 seatunnel 怎么配置Elasticsearch
  • ¥15 PSCAD安装问题 ERROR: Visual Studio 2013, 2015, 2017 or 2019 is not found in the system.
  • ¥15 (标签-MATLAB|关键词-多址)
  • ¥15 关于#MATLAB#的问题,如何解决?(相关搜索:信噪比,系统容量)
  • ¥500 52810做蓝牙接受端
  • ¥15 基于PLC的三轴机械手程序
  • ¥15 多址通信方式的抗噪声性能和系统容量对比
  • ¥15 winform的chart曲线生成时有凸起
  • ¥15 msix packaging tool打包问题
  • ¥15 finalshell节点的搭建代码和那个端口代码教程