通过标准库,我看到很多类似于以下的功能:
// src/database/sql/sql.go
func (dc *driverConn) removeOpenStmt(ds *driverStmt) {
dc.Lock()
defer dc.Unlock()
delete(dc.openStmt, ds)
}
...
func (db *DB) addDep(x finalCloser, dep interface{}) {
//println(fmt.Sprintf("addDep(%T %p, %T %p)", x, x, dep, dep))
db.mu.Lock()
defer db.mu.Unlock()
db.addDepLocked(x, dep)
}
// src/expvar/expvar.go
func (v *Map) addKey(key string) {
v.keysMu.Lock()
defer v.keysMu.Unlock()
v.keys = append(v.keys, key)
sort.Strings(v.keys)
}
// etc...
即:简单的功能,没有返回,并且可能无法恐慌,仍然推迟解锁其互斥锁。据我了解,defer
的开销已得到改善(可能仍处于改进阶段),但话虽如此:是否有理由包含defer
在这些功能?难道这些类型的延迟最终会减慢高流量功能吗?
答案 0 :(得分:8)
始终将Mutex.Unlock()
和WaitGroup.Done()
之类的内容推迟到函数顶部,这样可以更轻松地进行调试和维护,因为您可以立即看到这些部分已正确处理,因此您知道这些重要部分已经过精心处理的,并可以迅速转向其他问题。
在3行函数中并不是什么大不了的事,但是一致的代码查找代码也更容易阅读。然后随着代码的增长,您不必担心添加可能会出现恐慌的表达式,或者复杂的早期返回逻辑,因为预先存在的延迟将始终正常工作。
答案 1 :(得分:2)
恐慌突然(因此可能无法预测或毫无准备)违反正常控制流程。它可能会从任何东西中出现 - 通常来自外部事物 - 例如内存故障。 defer
机制提供了一种简单且非常便宜的工具来执行退出操作。因此,不要让系统处于破碎状态。这对于高负载应用程序中的锁定很重要,因为它有助于不丢失锁定的资源并将整个系统冻结一次。
如果在某些时候代码没有恐慌的地方(难以猜测这样的系统;)但事情正在发展。后来这个功能会更复杂,并且能够引起恐慌。
结论:Defer可以帮助您确保在“扭曲”的情况下您的功能将正确退出。同样重要的是它是面向未来的 - 针对不同故障的相同答复。
因此,将它们放入简单的功能中也是一种美食风格。作为一名程序员,你可以看到没有任何东西丢失。并且在代码中更加确定。