英文:
timeout on function and goroutine leak
问题
我帮你翻译一下:
我希望在名为 foo 的函数上设置一个超时。考虑以下代码:
func fooWithTimeout(d time.Duration) error {
ch := make(chan error, 1)
go func() {
ch <- foo()
}()
select {
case err := <-ch:
return err
case <-time.After(d):
return errors.New("foo has timed out")
}
}
- 如果
foo超时,那么它是否还能够向通道ch写入数据,或者存在阻塞或恐慌的风险吗? - 一旦
fooWithTimeout退出,通道ch会发生什么情况? - 这段代码有潜在的问题吗?
- 我应该在调用
foo之前在go func(){...}()中添加defer close(ch)吗? - 在这个例子中,使用缓冲(大小为1)通道和非缓冲通道有关系吗?
英文:
I wish to put a timeout on a function called foo. Consider the following
func fooWithTimeout(d time.Duration) error {
ch := make(chan error, 1)
go func() {
ch <- foo()
}()
select {
case err := <-ch:
return err
case <-time.After(d):
return errors.New("foo has timed out")
}
}
- If
foohas timed out, then willfooever be able to write to channelchor is there a risk the goroutine blocks or panics? - What happens to channel
choncefooWithTimeouthas exited? - Is this code potentially problematic?
- Should I add
defer close(ch)withingo func(){...}()just before callingfoo? - Does it matter that I use a buffered (with size 1) or an unbuffered channel in this example?
答案1
得分: 1
在计时器滴答后,fooWithTimeout将返回。goroutine将继续运行,直到foo返回。
如果foo超时,它将向缓冲通道ch写入数据。
如果foo返回,通道ch最终将被垃圾回收。
你不需要关闭通道。一旦它超出作用域,它将被垃圾回收。
大量调用fooWithTimeout将创建大量资源。每次调用都会创建两个goroutine。正确的超时处理方式是将foo更改为使用上下文(context)。
英文:
After the timer tick, fooWithTimeout will return. The goroutine will continue running until foo returns.
If foo times out, it will write to channel ch because it is buffered.
The channel ch will be garbage collected eventually if foo returns.
You don't need to close the channel. Once it is out of scope, it will be garbage collected.
A large burst of calls fooWithTimeout will create large amount of resources. Each call creates two goroutines. The proper way of timing this out is to change foo to use a context.
答案2
得分: 1
在https://stackoverflow.com/a/73611534/1079543的基础上,这里是带有上下文的foo函数的代码:
package main
import (
"context"
"fmt"
"log"
"time"
)
func foo(ctx context.Context) (string, error) {
ch := make(chan string, 1)
go func() {
fmt.Println("Sleeping...")
time.Sleep(time.Second * 1)
fmt.Println("Wake up...")
ch <- "foo"
}()
select {
case <-ctx.Done():
return "", fmt.Errorf("context cancelled: %w", ctx.Err())
case result := <-ch:
return result, nil
}
}
func main() {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
defer cancel()
res, err := foo(ctx)
if err != nil {
log.Fatalf("foo failed: %v", err)
}
log.Printf("res: %s", res)
}
英文:
Building on https://stackoverflow.com/a/73611534/1079543, here is foo with a context:
package main
import (
"context"
"fmt"
"log"
"time"
)
func foo(ctx context.Context) (string, error) {
ch := make(chan string, 1)
go func() {
fmt.Println("Sleeping...")
time.Sleep(time.Second * 1)
fmt.Println("Wake up...")
ch <- "foo"
}()
select {
case <-ctx.Done():
return "", fmt.Errorf("context cancelled: %w", ctx.Err())
case result := <-ch:
return result, nil
}
}
func main() {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
defer cancel()
res, err := foo(ctx)
if err != nil {
log.Fatalf("foo failed: %v", err)
}
log.Printf("res: %s", res)
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论