为了账号安全,请及时绑定邮箱和手机立即绑定

选定组中的通道在指定时间内没有接收到信号时跳出循环

选定组中的通道在指定时间内没有接收到信号时跳出循环

Go
Qyouu 2023-05-08 15:28:50
当且仅当我在特定时间段内我的 select 语句正在侦听的任何通道上都没有收到任何信号时,我该如何跳出包含 select 语句的惯用 Go for 循环。让我用一个例子来加强这个问题。设置:假设我有一个var listenCh <-chan string正在收听的频道。让我们假设其他一些 go 例程(不在我们的控制范围内)在此通道上发送不同的字符串。我对给定的字符串进行一些处理,然后在listenCh.要求:我想在 上的两个连续信号之间最多等待 10 秒(精度不重要)listenCh,然后再关闭我的操作(永久中断 for 循环)。代码存根: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    }  }}我将如何以最有效的方式实现我的要求。通常的退出通道技术time.After(time.Duration)似乎在一个循环后不会重置,因此即使有连续的值流,整个程序也会在 10 秒内关闭。我在 SO 上找到了问题的变体(但不是我想要的),但我看到的没有一个能回答我的特定用例。
查看完整描述

1 回答

?
心有法竹

TA贡献1866条经验 获得超5个赞

前言:使用time.Timer是推荐的方式,time.After()这里的使用只是为了演示和推理。请使用第二种方法。


使用time.After()(不推荐这样做)

如果你放入time.After()case 分支,那将在每次迭代中“重置”,因为每次都会返回一个新频道,这样就可以了:

func doingSomething(listenCh <-chan string) {

    for {

        select {

        case mystr := <-listenCh:

            log.Println("Received", mystr)

        case <-time.After(1 * time.Second):

            log.Println("Timeout")

            return

        }

    }

}

(我在 Go Playground 上使用 1 秒超时来提高可测试性。)


我们可以这样测试它:


ch := make(chan string)

go func() {

    for i := 0; i < 3; i++ {

        ch <- fmt.Sprint(i)

        time.Sleep(500 * time.Millisecond)

    }

}()

doingSomething(ch)

输出(在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

使用time.Timer(推荐方案)

如果从通道接收的速率很高,这可能会有点浪费资源,因为在后台创建并使用了一个新计时器,time.After()它不会神奇地停止并在不再需要时立即收集垃圾如果您在超时前从通道收到一个值。

time.Timer一个对资源更友好的解决方案是在循环之前创建一个循环,如果在超时之前收到一个值,则将其重置。

这就是它的样子:

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

        }

    }

}

测试和输出是一样的。在Go Playground试试这个。



查看完整回答
反对 回复 2023-05-08
  • 1 回答
  • 0 关注
  • 91 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信