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

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

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

问题

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

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

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

func myfunc(timeout time.Duration) {

	successChan := make(chan struct{})
	timeoutChan := make(chan struct{})

	// 持续监听任务
	go func() {
		for {
			select {
			// 如果已达到超时时间,则退出for循环
			case <-timeoutChan:
				return
			default:
			}
			// 如果任务已成功,则提前退出
			if checkSuccess() {
				close(successChan)
				return
			}
			time.Sleep(time.Second)
		}
	}()

	// 一旦超时,停止监听任务
	select {
	case <-time.After(timeout):
		close(timeoutChan)
		return
	case <-successChan:
		return
	}

	return
}

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

英文:

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.

func myfunc (timeout time.Duration) {

	successChan := make(chan struct{})
	timeoutChan := make(chan struct{})

	// Keep listening to the task.
	go func() {
		for {
			select {
            // Exit the forloop if the timeout has been reached
			case &lt;-timeoutChan:
				return
			default:
			}
			// Early exit if task has succeed.
			if checkSuccess() {
				close(successChan)
				return
			}
        time.Sleep(time.Second)
		}
	}()

	// Once timeout, stop listening to the task.
	select {
	case &lt;-time.After(timeout):
		close(timeoutChan)
        return 
	case &lt;-successChan:
		return
	}

	return
}

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

答案1

得分: 6

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

func myfunc(timeout time.Duration) {
   ticker := time.NewTicker(time.Second)
   defer ticker.Close()
   to := time.NewTimer(timeout)
   defer to.Stop()
   for {
      select {
         case <-to.C:
           return // 超时
         case <-ticker.C:
           if checkSuccess() {
             return
           }
      }
   }
}
英文:

You don't need a separate goroutine or channels:

func myfunc (timeout time.Duration) {
   ticker:=time.NewTicker(time.Second)
   defer ticker.Close()
   to:=time.NewTimer(timeout)
   defer to.Stop()
   for {
      select {
         case &lt;-to.C:
           return // timeout
         case &lt;-ticker:
           if checkSuccess() {
             return
           }
      }
   }
}

</details>



# 答案2
**得分**: 3

我更喜欢在这种情况下使用`context.Context`,这样实用函数可以更容易地适应各种真实场景。

我还会返回一些东西,比如`error`,例如从`ctx.Err()`或`bool`,以向调用者提供一些反馈(调用者可能决定无论如何都要丢弃它):

```go
func tryFunc(ctx context.Context, f func() bool) bool {
    ticker := time.NewTicker(time.Second)
    defer ticker.Stop()
    for {
        select {
        case <-ctx.Done():
            return false
        case <-ticker.C:
            if b := f(); b {
                return b
            }
        }
    }
}

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):

func tryFunc(ctx context.Context, f func() bool) bool {
	ticker := time.NewTicker(time.Second)
	defer ticker.Stop()
	for {
		select {
		case &lt;-ctx.Done():
			return false
		case &lt;-ticker.C:
			if b := f(); b {
				return b
			}
		}
	}
}

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:

确定