我想像https://github.com/im7mortal/kmutex那样编写一个具有键的互斥锁,但是要有不同的想法。只需将互斥锁存储在哈希图中。但是我的代码中总是存在一些死锁。如何找到错误?还是有更好的方法编写密钥互斥锁?
package kmutex
import (
"sync"
)
type keyMutex struct {
localLockMap map[string]*sync.Mutex
globalLock sync.Mutex
}
func NewKeyMutex() *keyMutex {
return &keyMutex{localLockMap: map[string]*sync.Mutex{}}
}
func (km *keyMutex) Lock(key string) {
km.globalLock.Lock()
wl, ok := km.localLockMap[key]
if !ok {
wl = &sync.Mutex{}
km.localLockMap[key] = wl
}
km.globalLock.Unlock()
wl.Lock()
}
func (km *keyMutex) Unlock(key string) {
km.globalLock.Lock()
wl, ok := km.localLockMap[key]
if !ok {
km.globalLock.Unlock()
return
}
delete(km.localLockMap, key)
km.globalLock.Unlock()
wl.Unlock()
}
并且测试代码在下面
func TestKeyMutex1(t *testing.T) {
keyMutex := kmutex.NewKeyMutex()
//var keyMutex sync.Mutex
var count = 0
var wg sync.WaitGroup
var num = 100
for i := 1; i <= num; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
keyMutex.Lock("a")
count += i
keyMutex.Unlock("a")
}(i)
}
wg.Wait()
println(count)
}
里面总是有死锁。删除delete(km.localLockMap, key)
行之后,死锁消失了!但是我还是听不懂
答案 0 :(得分:4)
问题确实是在映射中删除了互斥锁。
考虑到在同一项中只有三个锁(在您的情况下为"a"
),它们按以下顺序运行(当然,在多个goroutine中):
keyMutex.Lock("a") // lock1
keyMutex.Lock("a") // lock2
keyMutex.Lock("a") // lock3
keyMutex.Unlock("a") // unlock1
keyMutex.Unlock("a") // unlock2
keyMutex.Unlock("a") // unlock3
lock1
在映射中创建一个互斥锁,然后lock2
和lock3
获取相同的互斥锁,从而锁定互斥锁。 unlock1
找到互斥锁,然后将其解锁并从地图中删除,从而解除对lock2
(或lock3
,但出于讨论的考虑,假设它是lock2
)的阻塞。但是,当unlock2
或unlock3
运行时,它在映射中没有找到互斥体,什么也没有解锁,并保持lock3
处于阻塞状态,从而导致死锁。
从地图上删除互斥锁的行根本没有意义:互斥锁是可重复使用的,并且在上下文中,对于相同的条目只有相同的互斥锁才是明智的。
答案 1 :(得分:0)
您还可以检出sync.Map以及为您提取互斥体原语的位置。 从官方文档中,
Map类似于Go map [interface {}] interface {},但是可以安全地被多个goroutine并发使用,而无需额外的锁定或协调。加载,存储和删除以固定的固定时间运行。