我们有一个主要的go例程,该例程会生成一个父goroutine,而后者又会生成一个子go例程。
即使父母返回后,孩子goroutine仍然运行。这会导致goroutine泄漏。
我们如何避免这种情况?
下面,我添加了一个代码段来模拟以下内容 这里的子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()
}
答案 0 :(得分:0)
这是您的上下文相关代码段:https://play.golang.org/p/0TXyt4vuGKJ。
package main
import (
"context"
"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)
ctx, cancel := context.WithCancel(context.Background())
go parentRoutine(ctx)
wg.Wait()
cancel()
time.Sleep(time.Second) //If main immediately exists the child goroutine does not
//have the time to terminate.
fmt.Println("Completed parent - ", runtime.NumGoroutine())
}
func parentRoutine(ctx context.Context) {
fmt.Println("Starting parent function - ", runtime.NumGoroutine())
childRes := make(chan int)
// Spawning child goroutine
go func(ctx context.Context) {
// 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())
select {
case <-ctx.Done():
fmt.Println("Child's context expired - ", runtime.NumGoroutine())
case <-time.After(time.Duration(duration) * time.Second):
//time consuming task
}
fmt.Println("Child ended - ", runtime.NumGoroutine())
childRes <- 1
}(ctx)
select {
case <-ctx.Done():
fmt.Println("Parent's context expired - ", runtime.NumGoroutine())
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()
}
现在输出如下:
In main function - 1
Starting parent function - 2
Starting child function - 3
Child timed out - 3
Child's context expired - 2
Child ended - 2
Completed parent - 2