我正在查看一些现有的代码并重复几次
defer mtx.Unlock()
mtx.Lock()
Run Code Online (Sandbox Code Playgroud)
这对我来说是错误的,我更喜欢Unlock
在执行之后推迟执行的惯用方法,Lock
但是文档Mutex.Lock
没有指定Lock
将失败的情况.因此,早期defer
模式的行为应该与惯用的方式相同.
我的问题是:有没有令人信服的案例说这种模式是劣等的?(例如,Lock
可能会失败,然后是延期的Unlock
遗嘱panic
),因此代码应该被更改,还是应该保留原样?
简短回答:
好的,可以。该defer
调用是在函数返回后进行的(嗯,有点)。
更长、更细致的答案:
这是有风险的,应该避免。在您的 2 行代码片段中,这不会成为问题,但请考虑以下代码:
func (o *Obj) foo() error {
defer o.mu.Unlock()
if err := o.CheckSomething(); err != nil {
return err
}
o.mu.Lock()
// do stuff
}
Run Code Online (Sandbox Code Playgroud)
在这种情况下,互斥锁可能根本没有被锁定,或者更糟:它可能被另一个例程锁定,而您最终将其解锁。与此同时,另一个例程获得了它实际上不应该拥有的锁,并且您要么会出现数据争用,要么当该例程返回时,解锁调用将出现恐慌。调试这种混乱的情况是一场噩梦,并且应该不惜一切代价避免。
除了代码看起来直观并且更容易出错之外,根据您想要实现的目标,它defer
确实是有代价的。在大多数情况下,成本相当小,但如果您正在处理绝对时间紧迫的事情,通常最好在需要时手动添加解锁调用。我不使用 defer 的一个很好的例子是,如果您在 a 中缓存内容map[string]interface{}
:我会创建一个包含缓存值的结构体和一个sync.RWMutext
供并发使用的字段。如果我经常使用这个缓存,延迟调用可能会开始增加。它可能看起来有点混乱,但这是根据具体情况而定的。要么性能是您的目标,要么是更短、更具可读性的代码。
关于延迟的其他注意事项: