Go语言中使用context.WithCancel后,为何建议调用cancelFunc?Gin下能否移除defer?
关于context.WithCancel的cancelFunc执行问题
一、为什么建议在函数末尾执行cancelFunc?
用context.WithCancel创建的可取消上下文,依赖内部的Done通道传递取消信号。如果不主动调用cancelFunc,这个上下文及关联资源(比如监听Done通道的goroutine)不会被及时回收——哪怕父上下文已取消,子上下文的资源释放也会延迟。在高频调用的场景下,这种累积的资源占用会慢慢拖垮程序性能,甚至引发内存泄漏。
二、Gin框架下用defer cancelFunc()有没有必要?
必须有。Gin的请求上下文是请求级别的,请求结束后会被销毁,但这和你自己创建的子上下文是两码事:
- Gin的上下文取消是在请求处理完成后触发,但你用
WithCancel生成的cancelCtx,只有两种情况会被取消:要么你主动调用cancelFunc,要么父上下文(Gin的ctx)被取消。 - 如果你的函数逻辑里没有触发错误分支(比如所有goroutine都执行成功),就不会主动调用
cancelFunc,此时cancelCtx会一直持有父上下文的引用,直到Gin的上下文被取消。而defer cancelFunc()能确保函数执行完毕时立刻取消子上下文,及时释放资源,避免不必要的资源占用。
三、给定代码里的defer cancelFunc()能不能移除?
绝对不能。看你的代码逻辑:只有当某个goroutine执行myLogic()出错时,才会调用cancelFunc();如果100个goroutine全部执行成功,没有触发错误分支,cancelFunc()就不会被调用。此时如果没有defer cancelFunc(),cancelCtx就不会被取消,相关资源会一直占用到Gin的请求上下文被销毁,这会造成不必要的资源浪费,极端情况下会引发内存泄漏。
另外提两个代码里的小问题:
wg.add(1)要写成wg.Add(1)(Go语言方法名首字母需大写);- 循环内的goroutine没有捕获循环变量
i,虽然和当前问题无关,但容易引发逻辑问题,建议补上。
内容的提问来源于stack exchange,提问作者李明泽
相关产品推荐
相关产品推荐

