如何重复运行一个函数,直到它返回 true 或超时?

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

How to repetitively run a function until it returns true or timeout?

问题

我有一个名为checkSuccess()的函数,如果任务已完成,则返回true。

我想每隔1秒调用一次checkSuccess(),并在它返回true或超时时中断。

目前我使用一个goroutine来运行一个for循环,在其中每隔1秒调用一次checkSuccess()。在主进程中,我使用time.After来检查整个检查持续时间是否已超时。

  1. func myfunc(timeout time.Duration) {
  2. successChan := make(chan struct{})
  3. timeoutChan := make(chan struct{})
  4. // 持续监听任务
  5. go func() {
  6. for {
  7. select {
  8. // 如果已达到超时时间,则退出for循环
  9. case <-timeoutChan:
  10. return
  11. default:
  12. }
  13. // 如果任务已成功,则提前退出
  14. if checkSuccess() {
  15. close(successChan)
  16. return
  17. }
  18. time.Sleep(time.Second)
  19. }
  20. }()
  21. // 一旦超时,停止监听任务
  22. select {
  23. case <-time.After(timeout):
  24. close(timeoutChan)
  25. return
  26. case <-successChan:
  27. return
  28. }
  29. return
  30. }

实际上,这已经实现了我的目标,但我觉得它很繁琐。有没有更好(更短)的编写方式?

英文:

I have a function checkSuccess() which return true if the task have finished.

I'd like to call checkSuccess() every 1 second and break until it return true or timeout.

What I'm having now is to use a goroutine to run a for loop, in which I call checkSuccess() every 1 second. And in the main process, I use time.After to check if the whole checking duration has passed timeout.

  1. func myfunc (timeout time.Duration) {
  2. successChan := make(chan struct{})
  3. timeoutChan := make(chan struct{})
  4. // Keep listening to the task.
  5. go func() {
  6. for {
  7. select {
  8. // Exit the forloop if the timeout has been reached
  9. case &lt;-timeoutChan:
  10. return
  11. default:
  12. }
  13. // Early exit if task has succeed.
  14. if checkSuccess() {
  15. close(successChan)
  16. return
  17. }
  18. time.Sleep(time.Second)
  19. }
  20. }()
  21. // Once timeout, stop listening to the task.
  22. select {
  23. case &lt;-time.After(timeout):
  24. close(timeoutChan)
  25. return
  26. case &lt;-successChan:
  27. return
  28. }
  29. return
  30. }

It actually has achieved my goal, but I found it very tedious. Is there any better (shorter) way to write it?

答案1

得分: 6

你不需要单独的goroutine或通道:

  1. func myfunc(timeout time.Duration) {
  2. ticker := time.NewTicker(time.Second)
  3. defer ticker.Close()
  4. to := time.NewTimer(timeout)
  5. defer to.Stop()
  6. for {
  7. select {
  8. case <-to.C:
  9. return // 超时
  10. case <-ticker.C:
  11. if checkSuccess() {
  12. return
  13. }
  14. }
  15. }
  16. }
英文:

You don't need a separate goroutine or channels:

  1. func myfunc (timeout time.Duration) {
  2. ticker:=time.NewTicker(time.Second)
  3. defer ticker.Close()
  4. to:=time.NewTimer(timeout)
  5. defer to.Stop()
  6. for {
  7. select {
  8. case &lt;-to.C:
  9. return // timeout
  10. case &lt;-ticker:
  11. if checkSuccess() {
  12. return
  13. }
  14. }
  15. }
  16. }
  17. </details>
  18. # 答案2
  19. **得分**: 3
  20. 我更喜欢在这种情况下使用`context.Context`,这样实用函数可以更容易地适应各种真实场景。
  21. 我还会返回一些东西,比如`error`,例如从`ctx.Err()``bool`,以向调用者提供一些反馈(调用者可能决定无论如何都要丢弃它):
  22. ```go
  23. func tryFunc(ctx context.Context, f func() bool) bool {
  24. ticker := time.NewTicker(time.Second)
  25. defer ticker.Stop()
  26. for {
  27. select {
  28. case <-ctx.Done():
  29. return false
  30. case <-ticker.C:
  31. if b := f(); b {
  32. return b
  33. }
  34. }
  35. }
  36. }

Playground: https://go.dev/play/p/Iz_urEkBMIi

注意:确保上下文超时。context.Background()没有超时。

英文:

I prefer using context.Context in cases like this, so that the utility function can more easily be adapted to a variety of real world scenarios.

I would also return something, maybe error, e.g. from ctx.Err() or the bool, to give some feedback to the caller (the caller may decide to discard it anyway):

  1. func tryFunc(ctx context.Context, f func() bool) bool {
  2. ticker := time.NewTicker(time.Second)
  3. defer ticker.Stop()
  4. for {
  5. select {
  6. case &lt;-ctx.Done():
  7. return false
  8. case &lt;-ticker.C:
  9. if b := f(); b {
  10. return b
  11. }
  12. }
  13. }
  14. }

Playground: https://go.dev/play/p/Iz_urEkBMIi

PS: make sure the context has a timeout. context.Background() doesn't

huangapple
  • 本文由 发表于 2022年1月21日 05:22:51
  • 转载请务必保留本文链接:https://go.coder-hub.com/70793565.html
匿名

发表评论

匿名网友

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

确定