【问题标题】:For loop that breaks after n amount of seconds在 n 秒后中断的 for 循环
【发布时间】:2017-01-19 09:54:20
【问题描述】:

如何在这个简单的for 循环自执行后恰好经过一个 1 后中断?

var i int

for {
  i++
}

【问题讨论】:

    标签: for-loop go timeout


    【解决方案1】:

    通过检查自开始以来经过的时间:

    var i int
    for start := time.Now(); time.Since(start) < time.Second; {
        i++
    }
    

    或者使用“超时”频道,通过调用time.After() 获得。使用select 检查时间是否到了,但您必须添加一个default 分支,这样它将是一个非阻塞检查。如果时间到了,就退出循环。使用标签并从for 循环中断也非常重要,否则break 将直接从select 中断,这将是一个无限循环。

    loop:
        for timeout := time.After(time.Second); ; {
            select {
            case <-timeout:
                break loop
            default:
            }
            i++
        }
    

    注意:如果循环体还执行通信操作(如发送或接收),则使用超时通道可能是唯一可行的选择! (您可以在同一个select中列出超时检查和循环的通信操作。)

    我们可能会重写超时通道解决方案以不使用标签:

    for stay, timeout := true, time.After(time.Second); stay; {
        i++
        select {
        case <-timeout:
            stay = false
        default:
        }
    }
    

    优化

    我知道你的循环只是一个例子,但如果循环只做了一点点工作,那么在每次迭代中检查超时是不值得的。我们可能会重写第一个解决方案来检查超时,例如在这样的每 10 次迭代中:

    var i int
    for start := time.Now(); ; {
        if i % 10 == 0 {
            if time.Since(start) > time.Second {
                break
            }
        }
        i++
    }
    

    我们可以选择一个2的倍数的迭代次数,然后我们可以使用比余数检查更快的位掩码:

    var i int
    for start := time.Now(); ; {
        if i&0x0f == 0 { // Check in every 16th iteration
            if time.Since(start) > time.Second {
                break
            }
        }
        i++
    }
    

    我们也可以计算一次结束时间(循环必须结束的时候),然后你只需要将当前时间与这个比较:

    var i int
    for end := time.Now().Add(time.Second); ; {
        if i&0x0f == 0 { // Check in every 16th iteration
            if time.Now().After(end) {
                break
            }
        }
        i++
    }
    

    【讨论】:

    • 嘿,@icza 你能详细说明我将如何检查每 128 和 256 次迭代吗?我不会假装我可以理解位掩码,即使我已经阅读了一些使用它的代码。谢谢!!
    • @DavidCardoso 每 128 次迭代的位掩码为 127,每 256 次迭代的位掩码为 255。您也可以使用十进制表示。如果你想用十六进制写,它是0x7f0xff
    【解决方案2】:

    我知道这个问题有点老了,但下面可能对寻找类似情况的人有用:

    func keepCheckingSomething() (bool, error) {
        timeout := time.After(10 * time.Second)
        ticker := time.Tick(500 * time.Millisecond)
        // Keep trying until we're timed out or get a result/error
        for {
            select {
            // Got a timeout! fail with a timeout error
            case <-timeout:
                return false, errors.New("timed out")
            // Got a tick, we should check on checkSomething()
            case <-ticker:
                ok, err := checkSomething()
                if err != nil {
                // We may return, or ignore the error
                    return false, err
                // checkSomething() done! let's return
                } else if ok {
                    return true, nil
                }
                // checkSomething() isn't done yet, but it didn't fail either, let's try again
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2020-08-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-06-13
      相关资源
      最近更新 更多