【发布时间】:2019-05-21 16:56:03
【问题描述】:
我们有一个主 goroutine,它生成一个父 goroutine,而父 goroutine 又生成一个子 goroutine。
即使在父级返回后,子级 goroutine 仍然运行。这会导致 goroutine 泄漏。
我们如何避免这种情况?
下面我加了一个代码sn-p来模拟下面的 这里的子 goroutine 可以是任何长时间运行的进程,例如 db 查询、api 调用等
Program output:
In main function - 1
Starting parent function - 2
Starting child function - 3
Child timed out - 3
Completed parent - 2 // Implying that child goroutine is still running with main routine
package main
import (
"fmt"
"runtime"
"sync"
"time"
)
// WaitGroup used by main to wait for parent goroutine
var wg sync.WaitGroup
// Long duration process time
var duration = 100
func main() {
fmt.Println("In main function - ", runtime.NumGoroutine())
wg.Add(1)
go parentRoutine()
wg.Wait()
fmt.Println("Completed parent - ", runtime.NumGoroutine())
}
func parentRoutine() {
fmt.Println("Starting parent function - ", runtime.NumGoroutine())
childRes := make(chan int)
// Spawning child goroutine
go func() {
// Here the child is a simulation of a long running process which might take more time than expected timeout. It runs even after parent returns due to timeout
fmt.Println("Starting child function - ", runtime.NumGoroutine())
time.Sleep(time.Duration(duration)*time.Second)
fmt.Println("Child ended - ", runtime.NumGoroutine())
childRes <- 1
}()
select {
case <-childRes:
fmt.Println("Child completed - ", runtime.NumGoroutine())
case <- time.After(time.Duration(3)*time.Second):
fmt.Println("Child timed out - ", runtime.NumGoroutine())
}
wg.Done()
}
【问题讨论】:
-
Goroutines 通常没有“父级”,也不需要有一个“父级”来防止泄漏。您只需要确保您的 goroutine 不会被阻止退出。超时和取消的正常方法是使用
Context -
如何使用上下文来解决这个问题?你能分享一个代码sn-p
-
文档中有示例:
WithCancel、WithTimeout,此处还有一个帖子Go Concurrency Patterns: Context。您有什么具体问题? -
contextpkg 在 go 标准库中大量使用,用于任何网络/阻塞调用。所以你也应该使用它。它们与级联效果很好地链接 - 从而避免较低级别的代码知道如何实现更高级别的任务。 -
谁能把上面的代码sn-p修改成context如何解决问题?