在n秒后中断的for循环。

huangapple go评论71阅读模式
英文:

For loop that breaks after n amount of seconds

问题

如何在执行后过去1秒钟后使这个简单的for循环中断?

var i int

for {
  i++
}

你可以使用time包来实现这个目标。在每次循环迭代之后,你可以使用time.Sleep函数来暂停1秒钟,然后使用break语句来中断循环。

下面是修改后的代码:

import (
  "time"
)

var i int

for {
  i++
  time.Sleep(1 * time.Second)
  break
}

这样,循环将在执行后的1秒钟后中断。

英文:

How can I make this simple for loop break after exactly one 1s has passed since its execution?

var i int

for {
  i++
}

答案1

得分: 60

通过检查从开始到现在的经过时间:

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

或者使用一个"timeout"通道,通过调用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 { // 每16次迭代检查一次
        if time.Since(start) > time.Second {
            break
        }
    }
    i++
}

我们还可以一次计算出结束时间(循环必须结束的时间),然后只需将当前时间与其进行比较:

var i int
for end := time.Now().Add(time.Second); ; {
    if i&0x0f == 0 { // 每16次迭代检查一次
        if time.Now().After(end) {
            break
        }
    }
    i++
}
英文:

By checking the elapsed time since the start:

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

Or using a "timeout" channel, acquired by calling time.After(). Use select to check if time is up, but you must add a default branch so it will be a non-blocking check. If time is up, break from the loop. Also very important to use a label and break from the for loop, else break will just break from the select and it will be an endless loop.

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

Note: If the loop body also performs communication operations (like send or receive), using a timeout channel may be the only viable option! (You can list the timeout check and the loop's communication op in the same select.)

We may rewrite the timeout channel solution to not use a label:

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

Optimization

I know your loop is just an example, but if the loop is doing just a tiny bit of work, it is not worth checking the timeout in every iteration. We may rewrite the first solution to check timeout e.g. in every 10 iterations like this:

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

We may choose an iteration number which is a multiple of 2, and then we may use bitmasks which is supposed to be even faster than remainder check:

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

We may also calculate the end time once (when the loop must end), and then you just have to compare the current time to this:

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

答案2

得分: 15

我知道这个问题有点旧,但以下内容对于寻找类似场景的人可能有用:

func keepCheckingSomething() (bool, error) {
    timeout := time.NewTimer(10 * time.Second)
    ticker := time.NewTicker(500 * time.Millisecond)

    defer timeout.Stop()
    defer ticker.Stop()

    // 不断尝试,直到超时或获得结果/错误
    for {
        select {
        // 超时!返回超时错误
        case <-timeout.C:
            // 可能最后再检查一次
            ok, err := checkSomething()
            if !ok {
                return false, errors.New("超时")
            }
            return ok, err
        // 定时器滴答,我们应该检查 checkSomething()
        case <-ticker.C:
            ok, err := checkSomething()
            if err != nil {
                // 我们可以返回,或者忽略错误
                return false, err
            // checkSomething() 完成!返回结果
            } else if ok {
                return true, nil
            }
            // checkSomething() 还没有完成,但也没有失败,让我们再试一次
        }
    }
}

希望对你有帮助!

英文:

I know the question is a bit old, but below might be useful for someone looking for similar scenario:

func keepCheckingSomething() (bool, error) {
	timeout := time.NewTimer(10 * time.Second)
    ticker := time.NewTicker(500 * time.Millisecond)

    defer timeout.Stop()
    defer ticker.Stop()

	// Keep trying until we&#39;re timed out or get a result/error
	for {
		select {
		// Got a timeout! fail with a timeout error
		case &lt;-timeout:
		// maybe, check for one last time
            ok, err := checkSomething()
            if !ok {
    			return false, errors.New(&quot;timed out&quot;)
            }
			return ok, err
		// Got a tick, we should check on checkSomething()
		case &lt;-ticker:
			ok, err := checkSomething()
			if err != nil {
            // We may return, or ignore the error
				return false, err
			// checkSomething() done! let&#39;s return
			} else if ok {
				return true, nil
			}
			// checkSomething() isn&#39;t done yet, but it didn&#39;t fail either, let&#39;s try again
		}
	}
}

答案3

得分: 0

var i int
responseTimeout = 1 * time.Minute

deadline := time.Now().Add(responseTimeout)
for time.Now().Before(deadline) {
    i++
}
var i int
responseTimeout = 1 * time.Minute

deadline := time.Now().Add(responseTimeout)
for time.Now().Before(deadline) {
    i++
}

这段代码是使用Go语言编写的。它声明了一个整数变量i,并设置了一个响应超时时间为1分钟。然后,它使用当前时间加上超时时间来计算截止时间。接下来,它进入一个循环,只要当前时间在截止时间之前,就会不断地增加i的值。

英文:
var i int
responseTimeout = 1 * time.Minute

deadline := time.Now().Add(responseTimeout)
for time.Now().Before(deadline) {
    i++
}

huangapple
  • 本文由 发表于 2017年1月19日 17:54:20
  • 转载请务必保留本文链接:https://go.coder-hub.com/41738680.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定