在简单,无回报,无恐慌的功能中延迟是否有任何优势?

时间:2017-11-15 02:03:42

标签: go

通过标准库,我看到很多类似于以下的功能:

// 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在这些功能?难道这些类型的延迟最终会减慢高流量功能吗?

2 个答案:

答案 0 :(得分:8)

始终将Mutex.Unlock()WaitGroup.Done()之类的内容推迟到函数顶部,这样可以更轻松地进行调试和维护,因为您可以立即看到这些部分已正确处理,因此您知道这些重要部分已经过精心处理的,并可以迅速转向其他问题。

在3行函数中并不是什么大不了的事,但是一致的代码查找代码也更容易阅读。然后随着代码的增长,您不必担心添加可能会出现恐慌的表达式,或者复杂的早期返回逻辑,因为预先存在的延迟将始终正常工作。

答案 1 :(得分:2)

恐慌突然(因此可能无法预测或毫无准备)违反正常控制流程。它可能会从任何东西中出现 - 通常来自外部事物 - 例如内存故障。 defer机制提供了一种简单且非常便宜的工具来执行退出操作。因此,不要让系统处于破碎状态。这对于高负载应用程序中的锁定很重要,因为它有助于不丢失锁定的资源并将整个系统冻结一次。

如果在某些时候代码没有恐慌的地方(难以猜测这样的系统;)但事情正在发展。后来这个功能会更复杂,并且能够引起恐慌。

结论:Defer可以帮助您确保在“扭曲”的情况下您的功能将正确退出。同样重要的是它是面向未来的 - 针对不同故障的相同答复。

因此,将它们放入简单的功能中也是一种美食风格。作为一名程序员,你可以看到没有任何东西丢失。并且在代码中更加确定。