Go语言 我怎么能用响应式上下文取消来睡觉?

vlf7wbxs  于 2023-11-14  发布在  Go
关注(0)|答案(6)|浏览(134)

在Go语言中,我想在一段时间内执行time.Sleep(例如在重试之间等待),但如果上下文被取消(不仅是因为截止日期,也是手动取消),我想快速返回。
什么是正确的或最好的方法来做到这一点?谢谢!

8nuwlpux

8nuwlpux1#

您可以使用select来实现这一点:

  1. package main
  2. import (
  3. "fmt"
  4. "time"
  5. "context"
  6. )
  7. func main() {
  8. fmt.Println("Hello, playground")
  9. ctx, cancel := context.WithCancel(context.Background())
  10. defer cancel()
  11. go func(){
  12. t := time.Now()
  13. select{
  14. case <-ctx.Done(): //context cancelled
  15. case <-time.After(2 * time.Second): //timeout
  16. }
  17. fmt.Printf("here after: %v\n", time.Since(t))
  18. }()
  19. cancel() //cancel manually, comment out to see timeout kick in
  20. time.Sleep(3 * time.Second)
  21. fmt.Println("done")
  22. }

字符串
这里是Go-playground link

展开查看全部
iyzzxitl

iyzzxitl2#

你可以像其他人提到的那样使用select;但是,其他答案有一个bug,因为如果不清理timer.After(),它会泄漏内存。

  1. func SleepWithContext(ctx context.Context, d time.Duration) {
  2. timer := time.NewTimer(d)
  3. select {
  4. case <-ctx.Done():
  5. if !timer.Stop() {
  6. <-timer.C
  7. }
  8. case <-timer.C:
  9. }
  10. }

字符串

6ss1mwsb

6ss1mwsb3#

下面是一个sleepContext函数,你可以用它来代替time.Sleep

  1. func sleepContext(ctx context.Context, delay time.Duration) {
  2. select {
  3. case <-ctx.Done():
  4. case <-time.After(delay):
  5. }
  6. }

字符串
以下是一些示例用法(full runnable code on the Go Playground):

  1. func main() {
  2. ctx := context.Background()
  3. fmt.Println(time.Now())
  4. sleepContext(ctx, 1*time.Second)
  5. fmt.Println(time.Now())
  6. ctxTimeout, cancel := context.WithTimeout(ctx, 500*time.Millisecond)
  7. sleepContext(ctxTimeout, 1*time.Second)
  8. cancel()
  9. fmt.Println(time.Now())
  10. }

展开查看全部
eh57zj3b

eh57zj3b4#

time.After()函数存在以下问题:
在计时器触发之前,垃圾回收器不会恢复基础计时器。如果需要考虑效率,请改用NewTimer并调用Timer。如果不再需要计时器,请停止。
最好使用Timer对象并调用Stop()

  1. // Delay returns nil after the specified duration or error if interrupted.
  2. func Delay(ctx context.Context, d time.Duration) error {
  3. t := time.NewTimer(d)
  4. select {
  5. case <-ctx.Done():
  6. t.Stop()
  7. return fmt.Errorf("Interrupted")
  8. case <-t.C:
  9. }
  10. return nil
  11. }

字符串

展开查看全部
irlmq6kh

irlmq6kh5#

我通过将CancelContext和TimeoutContext结合起来做了类似的事情。
下面是示例代码:

  1. cancelCtx, cancel := context.WithCancel(context.Background())
  2. defer cancel()
  3. // The program "sleeps" for 5 seconds.
  4. timeoutCtx, _ := context.WithTimeout(cancelCtx, 5*time.Second)
  5. select {
  6. case <-timeoutCtx.Done():
  7. if cancelCtx.Err() != nil {
  8. log.Printf("Context cancelled")
  9. }
  10. }

字符串
this repo中你可以找到上述代码的完整用法。对不起,我的回答很简短,我还没有打开电脑,从电话上回答并不容易。

vojdkbi0

vojdkbi06#

根据您的使用情况,select{}是不需要的。

  1. sleep, cancel := context.WithTimeout(ctx, time.Second*5)
  2. defer cancel()
  3. <-sleep.Done()

字符串
<-sleep.Done()等待5秒或直到父ctx被取消,以先发生者为准。

相关问题