我有一个(LRU)缓存对象,我遇到了死锁......这怎么可能?
type cache struct {
mutex *sync.Mutex
...
}
func (this *cache) Init() { // guaranteed to be called once, in main()
this.mutex = &sync.Mutex{}
}
func (this *cache) f1() {
// Pattern for accessing mute, at the top of any function of 'cache' where needed.
this.mutex.Lock()
defer this.mutex.Unlock()
...
}
func (this *cache) f2() {
this.mutex.Lock()
defer this.mutex.Unlock()
...
}
在出现的每个函数mutex
中,仅使用此模式访问它。
然而......我陷入僵局。怎么可能呢?
注意:此代码已在生产服务器上运行了10个月,这是我第一次获得该代码。
编辑:所以f1()可以调用(间接)f2()来根据答案获得死锁。是的,但在我的代码中,这并没有发生,所以我真的很想知道
答案 0 :(得分:5)
如果cache
的一种方法调用另一种方法,并且两者都包含Lock()
调用,则很容易发生死锁。
见这个例子:
func (this *cache) f1() {
this.mutex.Lock()
defer this.mutex.Unlock()
this.f2()
}
func (this *cache) f2() {
this.mutex.Lock()
defer this.mutex.Unlock()
}
func main() {
c := &cache{}
c.Init()
c.f1()
fmt.Println("Hello, playground")
}
输出(在Go Playground上尝试):
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [semacquire]:
sync.runtime_SemacquireMutex(0x1040a12c, 0x8)
/usr/local/go/src/runtime/sema.go:62 +0x40
sync.(*Mutex).Lock(0x1040a128, 0x10429f5c)
/usr/local/go/src/sync/mutex.go:87 +0xa0
main.(*cache).f2(0x10429f94, 0x1100c0)
/tmp/sandbox647646735/main.go:23 +0x40
main.(*cache).f1(0x10429f94, 0xdf6e0)
/tmp/sandbox647646735/main.go:19 +0xa0
main.main()
/tmp/sandbox647646735/main.go:30 +0x60
请注意,不需要从一个方法直接调用另一个方法,也可以是传递调用。例如cache.f1()
可能会调用foo()
,这可能是一个独立的"功能,如果foo()
拨打cache.f2()
,我们就会陷入同样的僵局。
<强>改进:强>
不要为接收者this
命名,这不是惯用语。你可以简单地称它为c
。在此处阅读更多相关信息:In Go is naming the receiver variable 'self' misleading or good practice?
您可以嵌入互斥锁,方便使用,无需初始化。在此处阅读更多相关信息:When do you embed mutex in struct in Go?
type cache struct {
sync.Mutex
}
func (c *cache) f1() {
c.Lock()
defer c.Unlock()
c.f2()
}
func (c *cache) f2() {
c.Lock()
defer c.Unlock()
}
func main() {
c := &cache{}
c.f1()
fmt.Println("Hello, playground")
}
当然这也会造成僵局。在Go Playground上试一试。另请注意,这固有地暴露了互斥体(因为嵌入式类型以lowecae字母开头),因此任何人都可以调用Lock()
和Unlock()
方法。取决于案件是否是一个问题。