英文:
How to get time.Tick to tick immediately
问题
我有一个循环,直到作业启动并运行才停止:
ticker := time.NewTicker(time.Second * 2)
defer ticker.Stop()
started := time.Now()
for now := range ticker.C {
job, err := client.Job(jobID)
switch err.(type) {
case DoesNotExistError:
continue
case InternalError:
return err
}
if job.State == "running" {
break
}
if now.Sub(started) > time.Minute*2 {
return fmt.Errorf("timed out waiting for job")
}
}
在生产环境中运行得很好。唯一的问题是它使得我的测试变慢。它们在完成之前都要等待至少2秒钟。有没有办法让 time.Tick
立即触发?
英文:
I have a loop that iterates until a job is up and running:
ticker := time.NewTicker(time.Second * 2)
defer ticker.Stop()
started := time.Now()
for now := range ticker.C {
job, err := client.Job(jobID)
switch err.(type) {
case DoesNotExistError:
continue
case InternalError:
return err
}
if job.State == "running" {
break
}
if now.Sub(started) > time.Minute*2 {
return fmt.Errorf("timed out waiting for job")
}
}
Works great in production. The only problem is that it makes my tests slow. They all wait at least 2 seconds before completing. Is there anyway to get time.Tick
to tick immediately?
答案1
得分: 67
很遗憾,根据这个链接,Go开发者似乎不会在可预见的未来中添加这样的功能,所以我们必须应对...
有两种常见的使用ticker的方式:
for
循环
给定以下代码:
ticker := time.NewTicker(period)
defer ticker.Stop()
for <- ticker.C {
...
}
使用:
ticker := time.NewTicker(period)
defer ticker.Stop()
for ; true; <- ticker.C {
...
}
for
-select
循环
给定以下代码:
interrupt := make(chan os.Signal, 1)
signal.Notify(interrupt, os.Interrupt)
ticker := time.NewTicker(period)
defer ticker.Stop()
loop:
for {
select {
case <- ticker.C:
f()
case <- interrupt:
break loop
}
}
使用:
interrupt := make(chan os.Signal, 1)
signal.Notify(interrupt, os.Interrupt)
ticker := time.NewTicker(period)
defer ticker.Stop()
loop:
for {
f()
select {
case <- ticker.C:
continue
case <- interrupt:
break loop
}
}
为什么不直接使用time.Tick()
?
虽然Tick对于不需要关闭Ticker的客户端很有用,但请注意,如果没有关闭它的方法,底层的Ticker将无法被垃圾回收器回收;它会"泄漏"。
https://golang.org/pkg/time/#Tick
英文:
Unfortunately, it seems that Go developers will not add such functionality in any foreseeable future, so we have to cope...
There are two common ways to use tickers:
for
loop
Given something like this:
ticker := time.NewTicker(period)
defer ticker.Stop()
for <- ticker.C {
...
}
Use:
ticker := time.NewTicker(period)
defer ticker.Stop()
for ; true; <- ticker.C {
...
}
for
-select
loop
Given something like this:
interrupt := make(chan os.Signal, 1)
signal.Notify(interrupt, os.Interrupt)
ticker := time.NewTicker(period)
defer ticker.Stop()
loop:
for {
select {
case <- ticker.C:
f()
case <- interrupt:
break loop
}
}
Use:
interrupt := make(chan os.Signal, 1)
signal.Notify(interrupt, os.Interrupt)
ticker := time.NewTicker(period)
defer ticker.Stop()
loop:
for {
f()
select {
case <- ticker.C:
continue
case <- interrupt:
break loop
}
}
Why not just use time.Tick()
?
> While Tick is useful for clients that have no need to shut down the Ticker, be aware that without a way to shut it down the underlying Ticker cannot be recovered by the garbage collector; it "leaks".
答案2
得分: 24
以下是要翻译的内容:
ticker := time.NewTicker(period)
for ; true; <-ticker.C {
...
}
https://github.com/golang/go/issues/17601
翻译结果:
ticker := time.NewTicker(period)
for ; true; <-ticker.C {
...
}
https://github.com/golang/go/issues/17601
英文:
ticker := time.NewTicker(period)
for ; true; <-ticker.C {
...
}
答案3
得分: 6
Ticker
的实际实现在内部相当复杂。但是你可以用goroutine来包装它:
func NewTicker(delay, repeat time.Duration) *time.Ticker {
ticker := time.NewTicker(repeat)
oc := ticker.C
nc := make(chan time.Time, 1)
go func() {
nc <- time.Now()
for tm := range oc {
nc <- tm
}
}()
ticker.C = nc
return ticker
}
Ticker
的实际实现在内部相当复杂。但是你可以用goroutine来包装它:
func NewTicker(delay, repeat time.Duration) *time.Ticker {
ticker := time.NewTicker(repeat)
oc := ticker.C
nc := make(chan time.Time, 1)
go func() {
nc <- time.Now()
for tm := range oc {
nc <- tm
}
}()
ticker.C = nc
return ticker
}
英文:
The actual implementation of Ticker
internally is pretty complicated. But you can wrap it with a goroutine:
func NewTicker(delay, repeat time.Duration) *time.Ticker {
ticker := time.NewTicker(repeat)
oc := ticker.C
nc := make(chan time.Time, 1)
go func() {
nc <- time.Now()
for tm := range oc {
nc <- tm
}
}()
ticker.C = nc
return ticker
}
答案4
得分: 6
如果您想立即检查作业,请不要将ticker作为for循环中的条件。例如:
ticker := time.NewTicker(2 * time.Second)
defer ticker.Stop()
started := time.Now()
for {
job, err := client.Job(jobID)
if err == InternalError {
return err
}
if job.State == "running" {
break
}
now := <-ticker.C
if now.Sub(started) > 2*time.Minute {
return fmt.Errorf("等待作业超时")
}
}
如果您确实需要检查DoesNotExistError
,请确保在ticker之后进行检查,以免出现忙等待的情况。
英文:
If you want to check the job right away, don't use the ticker as the condition in the for loop. For example:
ticker := time.NewTicker(2 * time.Second)
defer ticker.Stop()
started := time.Now()
for {
job, err := client.Job(jobID)
if err == InternalError {
return err
}
if job.State == "running" {
break
}
now := <-ticker.C
if now.Sub(started) > 2*time.Minute {
return fmt.Errorf("timed out waiting for job")
}
}
If you do still need to check for DoesNotExistError
, you want to make sure you do it after the ticker so you don't have a busy-wait.
答案5
得分: 2
我烹饪了这样的东西
func main() {
t := time.Now()
callme := func() {
// 做更多的事情
fmt.Println("callme", time.Since(t))
}
ticker := time.NewTicker(10 * time.Second)
first := make(chan bool, 1)
first <- true
for {
select {
case <-ticker.C:
callme()
case <-first:
callme()
}
t = time.Now()
}
close(first)
}
英文:
I cooked up something like this
func main() {
t := time.Now()
callme := func() {
// do somethign more
fmt.Println("callme", time.Since(t))
}
ticker := time.NewTicker(10 * time.Second)
first := make(chan bool, 1)
first <- true
for {
select {
case <-ticker.C:
callme()
case <-first:
callme()
}
t = time.Now()
}
close(first)
}
答案6
得分: 1
我认为这可能是for-select
循环的一个有趣替代方案,特别是当case
的内容不是一个简单的函数时:
原始代码如下:
interrupt := make(chan os.Signal, 1)
signal.Notify(interrupt, os.Interrupt)
ticker := time.NewTicker(period)
defer ticker.Stop()
loop:
for {
select {
case <- ticker.C:
f()
case <- interrupt:
break loop
}
}
使用以下代码进行修改:
interrupt := make(chan os.Signal, 1)
signal.Notify(interrupt, os.Interrupt)
ticker := time.NewTicker(period)
defer ticker.Stop()
firstTick := false
// 创建一个包装了ticker的函数,使其在第一次立即触发
tickerChan := func() <-chan time.Time {
if !firstTick {
firstTick = true
c := make(chan time.Time, 1)
c <- time.Now()
return c
}
return ticker.C
}
loop:
for {
select {
case <- tickerChan():
f()
case <- interrupt:
break loop
}
}
英文:
I think this might be an interesting alternative for the for-select
loop, specially if the contents of the case
are not a simple function:
Having:
interrupt := make(chan os.Signal, 1)
signal.Notify(interrupt, os.Interrupt)
ticker := time.NewTicker(period)
defer ticker.Stop()
loop:
for {
select {
case <- ticker.C:
f()
case <- interrupt:
break loop
}
}
Use:
interrupt := make(chan os.Signal, 1)
signal.Notify(interrupt, os.Interrupt)
ticker := time.NewTicker(period)
defer ticker.Stop()
firstTick := false
// create a wrapper of the ticker that ticks the first time immediately
tickerChan := func() <-chan time.Time {
if !firstTick {
firstTick = true
c := make(chan time.Time, 1)
c <- time.Now()
return c
}
return ticker.C
}
loop:
for {
select {
case <- tickerChan():
f()
case <- interrupt:
break loop
}
}
答案7
得分: 0
使用Timer而不是Ticker如何?Timer可以以零持续时间启动,然后重置为所需的持续时间值:
timer := time.NewTimer(0)
defer timer.Stop()
for {
select {
case <-timer.C:
timer.Reset(interval)
job()
case <-ctx.Done():
break
}
}
Timer和Ticker都是Go语言中用于定时任务的工具。它们都可以在指定的时间间隔内重复执行某个任务。不同之处在于,Timer只会执行一次任务,而Ticker会重复执行任务。
在上面的代码中,我们使用Timer来执行任务。首先,我们创建一个Timer对象,并将其持续时间设置为0,这意味着它立即触发。然后,我们使用defer语句在函数结束时停止Timer,以确保资源被正确释放。
在无限循环中,我们使用select语句监听两个channel:timer.C和ctx.Done()。当timer.C的channel接收到值时,表示定时器已经触发,我们重置Timer的持续时间为interval,并执行job()函数。当ctx.Done()的channel接收到值时,表示上下文已经被取消,我们退出循环。
这样,我们就可以使用Timer来实现定时任务的执行。
英文:
How about using Timer instead of Ticker? Timer can be started with zero duration and then reset to the desired duration value:
timer := time.NewTimer(0)
defer timer.Stop()
for {
select {
case <-timer.C:
timer.Reset(interval)
job()
case <-ctx.Done():
break
}
}
</details>
# 答案8
**得分**: 0
你也可以在循环结束时关闭通道:
```golang
t := time.NewTicker(period)
defer t.Stop()
for {
...
<-t.C
}
英文:
You can also drain the channel at the end of the loop:
t := time.NewTicker(period)
defer t.Stop()
for {
...
<-t.C
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论