英文:
Is this func possible to cause goroutine leak
问题
func startTimer(ctx context.Context, intervalTime int) {
intervalChan := make(chan bool)
go func() {
for {
select {
case <-ctx.Done():
return
case <-time.After(time.Second * time.Duration(intervalTime)):
intervalChan <- true
}
}
}()
for {
select {
case <-ctx.Done():
return
case <-intervalChan:
doSomething()
}
}
}
你好,我写了上面的一个函数,想知道是否可能导致 goroutine 泄漏。
例如,第一个 select 语句向 intervalChan 发送了一个 true,然后第二个 select 语句从 ctx.Done() 接收到 Done 标志并返回。这个 goroutine 会一直阻塞吗?
英文:
func startTimer(ctx context.Context, intervalTime int) {
intervalChan := make(chan bool)
go func() {
for {
select {
case <-ctx.Done():
return
case <-time.After(time.Second * time.Duration(intervalTime)):
intervalChan <- true
}
}
}()
for {
select {
case <-ctx.Done():
return
case <-intervalChan:
doSomething()
}
}
Hi,I write a func as above and want to know is it possible to cause goroutine leak.
For example, the first select statement sends a true to intervalChan, then the second select statement receives Done flag from ctx.Done() and return. Will the goroutine be block forever?
答案1
得分: 1
我无法每次复制这种行为,但可能是某种泄漏。如果doSomething
执行一些重计算,同时goroutine在intervalChan <- true
上被阻塞,因为它无法推送到通道中。在doSomething
执行完成并且上下文被取消之后,startTimer在goroutine之前退出,这将导致goroutine被阻塞,因为没有任何intervalChan
的消费者。
go version go1.8.3 darwin/amd64
package main
import (
"context"
"fmt"
"time"
)
func startTimer(ctx context.Context, intervalTime int) {
intervalChan := make(chan bool)
go func() {
for {
select {
case <-ctx.Done():
fmt.Println("Done from inside of goroutine.")
return
case <-time.After(time.Second * time.Duration(intervalTime)):
fmt.Println("Interval reached.")
intervalChan <- true
}
}
}()
for {
select {
case <-ctx.Done():
fmt.Println("Done from startTimer.")
return
case <-intervalChan:
time.Sleep(10 * time.Second)
fmt.Println("Done")
}
}
}
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
startTimer(ctx, 2)
}
英文:
I cannot replicate this behaviour every time but could be some leak. If doSomething
do some heavy computation, meanwhile goroutine is blocked on intervalChan <- true
since it cannot push into the channel. After doSomething
finish execution and context was cancelled, startTimer exists before goroutine and this will lead into blocked goroutine, because there isn't any consumer of intervalChan
.
go version go1.8.3 darwin/amd64
package main
import (
"context"
"fmt"
"time"
)
func startTimer(ctx context.Context, intervalTime int) {
intervalChan := make(chan bool)
go func() {
for {
select {
case <-ctx.Done():
fmt.Println("Done from inside of goroutine.")
return
case <-time.After(time.Second * time.Duration(intervalTime)):
fmt.Println("Interval reached.")
intervalChan <- true
}
}
}()
for {
select {
case <-ctx.Done():
fmt.Println("Done from startTimer.")
return
case <-intervalChan:
time.Sleep(10 * time.Second)
fmt.Println("Done")
}
}
}
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
startTimer(ctx, 2)
}
答案2
得分: 1
你的第一个goroutine可能会被无限期地阻塞在intervalChan <- true
这一行。为了能够取消该发送操作,将其放在另一个select块中:
go func() {
for {
select {
case <-ctx.Done():
return
case <-time.After(time.Second * time.Duration(intervalTime)):
select {
case <-ctx.Done():
return
case intervalChan <- true:
}
}
}
}()
英文:
The only place your first goroutine could be blocked indefinitely is in intervalChan <- true
. Put it in another select block to be able to cancel that send:
go func() {
for {
select {
case <-ctx.Done():
return
case <-time.After(time.Second * time.Duration(intervalTime)):
select {
case <-ctx.Done():
return
case intervalChan <- true:
}
}
}
}()
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论