在Go语言中,我想在一段时间内执行time.Sleep(例如在重试之间等待),但如果上下文被取消(不仅是因为截止日期,也是手动取消),我想快速返回。什么是正确的或最好的方法来做到这一点?谢谢!
time.Sleep
8nuwlpux1#
您可以使用select来实现这一点:
select
package mainimport ( "fmt" "time" "context")func main() { fmt.Println("Hello, playground") ctx, cancel := context.WithCancel(context.Background()) defer cancel() go func(){ t := time.Now() select{ case <-ctx.Done(): //context cancelled case <-time.After(2 * time.Second): //timeout } fmt.Printf("here after: %v\n", time.Since(t)) }() cancel() //cancel manually, comment out to see timeout kick in time.Sleep(3 * time.Second) fmt.Println("done")}
package main
import (
"fmt"
"time"
"context"
)
func main() {
fmt.Println("Hello, playground")
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
go func(){
t := time.Now()
select{
case <-ctx.Done(): //context cancelled
case <-time.After(2 * time.Second): //timeout
}
fmt.Printf("here after: %v\n", time.Since(t))
}()
cancel() //cancel manually, comment out to see timeout kick in
time.Sleep(3 * time.Second)
fmt.Println("done")
字符串这里是Go-playground link
iyzzxitl2#
你可以像其他人提到的那样使用select;但是,其他答案有一个bug,因为如果不清理timer.After(),它会泄漏内存。
timer.After()
func SleepWithContext(ctx context.Context, d time.Duration) { timer := time.NewTimer(d) select { case <-ctx.Done(): if !timer.Stop() { <-timer.C } case <-timer.C: }}
func SleepWithContext(ctx context.Context, d time.Duration) {
timer := time.NewTimer(d)
select {
case <-ctx.Done():
if !timer.Stop() {
<-timer.C
case <-timer.C:
字符串
6ss1mwsb3#
下面是一个sleepContext函数,你可以用它来代替time.Sleep:
sleepContext
func sleepContext(ctx context.Context, delay time.Duration) { select { case <-ctx.Done(): case <-time.After(delay): }}
func sleepContext(ctx context.Context, delay time.Duration) {
case <-time.After(delay):
字符串以下是一些示例用法(full runnable code on the Go Playground):
func main() { ctx := context.Background() fmt.Println(time.Now()) sleepContext(ctx, 1*time.Second) fmt.Println(time.Now()) ctxTimeout, cancel := context.WithTimeout(ctx, 500*time.Millisecond) sleepContext(ctxTimeout, 1*time.Second) cancel() fmt.Println(time.Now())}
ctx := context.Background()
fmt.Println(time.Now())
sleepContext(ctx, 1*time.Second)
ctxTimeout, cancel := context.WithTimeout(ctx, 500*time.Millisecond)
sleepContext(ctxTimeout, 1*time.Second)
cancel()
型
eh57zj3b4#
time.After()函数存在以下问题:在计时器触发之前,垃圾回收器不会恢复基础计时器。如果需要考虑效率,请改用NewTimer并调用Timer。如果不再需要计时器,请停止。最好使用Timer对象并调用Stop():
time.After()
Timer
Stop()
// Delay returns nil after the specified duration or error if interrupted.func Delay(ctx context.Context, d time.Duration) error { t := time.NewTimer(d) select { case <-ctx.Done(): t.Stop() return fmt.Errorf("Interrupted") case <-t.C: } return nil}
// Delay returns nil after the specified duration or error if interrupted.
func Delay(ctx context.Context, d time.Duration) error {
t := time.NewTimer(d)
t.Stop()
return fmt.Errorf("Interrupted")
case <-t.C:
return nil
irlmq6kh5#
我通过将CancelContext和TimeoutContext结合起来做了类似的事情。下面是示例代码:
cancelCtx, cancel := context.WithCancel(context.Background())defer cancel()// The program "sleeps" for 5 seconds.timeoutCtx, _ := context.WithTimeout(cancelCtx, 5*time.Second)select {case <-timeoutCtx.Done(): if cancelCtx.Err() != nil { log.Printf("Context cancelled") }}
cancelCtx, cancel := context.WithCancel(context.Background())
// The program "sleeps" for 5 seconds.
timeoutCtx, _ := context.WithTimeout(cancelCtx, 5*time.Second)
case <-timeoutCtx.Done():
if cancelCtx.Err() != nil {
log.Printf("Context cancelled")
字符串在this repo中你可以找到上述代码的完整用法。对不起,我的回答很简短,我还没有打开电脑,从电话上回答并不容易。
vojdkbi06#
根据您的使用情况,select{}是不需要的。
select{}
sleep, cancel := context.WithTimeout(ctx, time.Second*5)defer cancel()<-sleep.Done()
sleep, cancel := context.WithTimeout(ctx, time.Second*5)
<-sleep.Done()
字符串<-sleep.Done()等待5秒或直到父ctx被取消,以先发生者为准。
ctx
6条答案
按热度按时间8nuwlpux1#
您可以使用
select
来实现这一点:字符串
这里是Go-playground link
iyzzxitl2#
你可以像其他人提到的那样使用
select
;但是,其他答案有一个bug,因为如果不清理timer.After()
,它会泄漏内存。字符串
6ss1mwsb3#
下面是一个
sleepContext
函数,你可以用它来代替time.Sleep
:字符串
以下是一些示例用法(full runnable code on the Go Playground):
型
eh57zj3b4#
time.After()
函数存在以下问题:在计时器触发之前,垃圾回收器不会恢复基础计时器。如果需要考虑效率,请改用NewTimer并调用Timer。如果不再需要计时器,请停止。
最好使用
Timer
对象并调用Stop()
:字符串
irlmq6kh5#
我通过将CancelContext和TimeoutContext结合起来做了类似的事情。
下面是示例代码:
字符串
在this repo中你可以找到上述代码的完整用法。对不起,我的回答很简短,我还没有打开电脑,从电话上回答并不容易。
vojdkbi06#
根据您的使用情况,
select{}
是不需要的。字符串
<-sleep.Done()
等待5秒或直到父ctx
被取消,以先发生者为准。