英文:
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
foo
has timed out, then willfoo
ever be able to write to channelch
or is there a risk the goroutine blocks or panics? - What happens to channel
ch
oncefooWithTimeout
has 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)
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论