英文:
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 <-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 <-time.After(timeout):
close(timeoutChan)
return
case <-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 <-to.C:
return // timeout
case <-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 <-ctx.Done():
return false
case <-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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论