Go语言出错时有条件延迟清理资源的通用惯用写法有哪些?
Go多资源初始化防泄漏惯用方案
下面是两种Go社区广泛使用的通用解决方案,适配不同场景:
方案1:为Bundle实现统一销毁方法
这种方式适合资源归属清晰、Bundle生命周期可独立管理的场景,逻辑高度聚合,后续新增资源只需修改销毁方法即可。
首先为Bundle实现Destroy方法,封装所有内部资源的销毁逻辑:
func (b *Bundle) Destroy() { // 按创建逆序销毁资源,符合资源释放的通用惯例 if b.c != nil { b.c.Destroy() } if b.b != nil { b.b.Destroy() } if b.a != nil { b.a.Destroy() } // 可补充资源置空逻辑避免野指针 b.a, b.b, b.c = nil, nil, nil }
改造NewBundle逻辑,提前通过defer挂接错误时的销毁逻辑:
func NewBundle() (Bundle, error) { var bundle Bundle var err error // 提前注册清理逻辑,只要返回时err非空就会执行销毁 defer func() { if err != nil { bundle.Destroy() } }() if bundle.a, err = GetExpensiveResource(); err != nil { return Bundle{}, err } if bundle.b, err = GetAnotherExpensiveResource(); err != nil { return Bundle{}, err } if bundle.c, err = GetAThirdExpensiveResource(); err != nil { return Bundle{}, err } return bundle, nil }
方案2:用清理函数列表统一管理
这种方式无需为结构体额外实现销毁方法,适合临时场景、或者资源销毁逻辑不统一的场景,灵活性更高。
func NewBundle() (Bundle, error) { var bundle Bundle var err error // 存储已创建资源的销毁函数 var cleanups []func() // 出错时逆序执行所有已注册的清理函数 defer func() { if err != nil { // 逆序执行和创建顺序相反,符合资源释放惯例 for i := len(cleanups)-1; i >= 0; i-- { cleanups[i]() } } }() bundle.a, err = GetExpensiveResource() if err != nil { return Bundle{}, err } cleanups = append(cleanups, bundle.a.Destroy) bundle.b, err = GetAnotherExpensiveResource() if err != nil { return Bundle{}, err } cleanups = append(cleanups, bundle.b.Destroy) bundle.c, err = GetAThirdExpensiveResource() if err != nil { return Bundle{}, err } cleanups = append(cleanups, bundle.c.Destroy) // 所有资源初始化成功,清空清理列表避免误销毁 cleanups = nil return bundle, nil }
两种方案都比单资源单独封装闭包的写法更易维护,扩展性也更好,是Go语言处理这类多资源初始化场景的通用惯用实现。
内容的提问来源于stack exchange,提问作者decitrig
相关产品推荐
相关产品推荐

