goroutine的回收机制 goroutine的回收是自动进行的,但是有前提条件:
自然退出 :goroutine运行的函数return之后,goroutine状态变为dead。释放资源 :栈内存归还runtime内存管理器 对应的G对象可能被放入缓存池以便复用 垃圾回收(GC)清理 :当 dead goroutine 没有任何引用时,会在 GC 中彻底释放内存。如果goroutine一直被阻塞或者被引用,它将无法回收,造成资源泄漏(goroutine leak) .
例如:
1 2 3 4 5 6 7 8 9 10 11 func leakyFunction () { ch := make (chan int ) go func () { val := <-ch fmt.Println(val) }() }
上述代码中的goroutine因为等不到数据,一直阻塞无法结束,也就无法被GC回收。
如何避免goroutine泄漏 使用context控制goroutine生命周期 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 package mainimport ( "context" "fmt" "time" ) func main () { defer fmt.Println("main exit" ) ctx, cancel := context.WithCancel(context.Background()) go func () { defer fmt.Println("cancelled" ) for { select { case <-ctx.Done(): fmt.Println("goroutine exit" ) return default : fmt.Println("do something else" ) } } }() time.Sleep(time.Millisecond) cancel() }
用waitgroup等待goroutine完成 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package mainimport ( "fmt" "sync" ) func main () { var wg sync.WaitGroup wg.Add(1 ) go func () { defer wg.Done() fmt.Println("task done" ) }() wg.Wait() fmt.Println("main done" ) }
用workpool复用goroutine 极简workpool:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package mainimport ( "fmt" "time" ) func main () { jobs := make (chan int , 100 ) for w := 0 ; w < 5 ; w++ { go func (id int ) { for j := range jobs { fmt.Printf("worker %d processing job %d\n" , id, j) } }(w) } for i := 0 ; i < 10 ; i++ { jobs <- i } close (jobs) time.Sleep(time.Second) }