如何通过无缺陷的密钥编写互斥体?

时间:2019-05-05 02:23:54

标签: go mutex

我想像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)行之后,死锁消失了!但是我还是听不懂

2 个答案:

答案 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在映射中创建一个互斥锁,然后lock2lock3获取相同的互斥锁,从而锁定互斥锁。 unlock1找到互斥锁,然后将其解锁并从地图中删除,从而解除对lock2(或lock3,但出于讨论的考虑,假设它是lock2)的阻塞。但是,当unlock2unlock3运行时,它在映射中没有找到互斥体,什么也没有解锁,并保持lock3处于阻塞状态,从而导致死锁。

从地图上删除互斥锁的行根本没有意义:互斥锁是可重复使用的,并且在上下文中,对于相同的条目只有相同的互斥锁才是明智的。

答案 1 :(得分:0)

您还可以检出sync.Map以及为您提取互斥体原语的位置。 从官方文档中,

  

Map类似于Go map [interface {}] interface {},但是可以安全地被多个goroutine并发使用,而无需额外的锁定或协调。加载,存储和删除以固定的固定时间运行。