Как настроить мьютекс на основе ключей (блокировка и разблокировка) в Golang?

# #multithreading #go #concurrency #mutex

Вопрос:

Предположим, у меня есть набор гороутинов

 wg := sync.WaitGroup{}
wg.Add(5)
go somefunction("A", amp;wg) //1
go somefunction("B", amp;wg) //2
go somefunction("A", amp;wg) //3
go somefunction("A", amp;wg) //4
go somefunction("B", amp;wg) //5

wg.Wait()
 

Мне требуется, чтобы одновременно выполнялась только одна подпрограмма определенной строки («A» или «B» здесь). В любое время должна выполняться только одна из некоторых функций(«A», amp;wg). Например, //1 и //2 запускаются одновременно. После завершения //2 запускается //5. После завершения //1 запускается любой из //3 или //4.

Я думал о разработке мьютекса на основе ключей, чтобы решить эту проблему.

 somefunction(key string){
   Lock(key)
   //piece of code
   Unlock(key)
}

 

Кусочек кода будет заблокирован для конкретного ключа здесь.

Комментарии:

1. Вы можете сделать это с map[string]*sync.Mutex помощью А. Но вам нужно быть осторожным, так как карта небезопасна для одновременных операций записи и чтения. Таким образом, ваши Lock(key) Unlock(key) методы и также должны быть закрыты другим sync.Mutex , если вы ожидаете, что ключи изменятся во время работы.

Ответ №1:

Давайте somefunction возьмем параметр мьютекса и передадим один и тот же экземпляр мьютекса для одного и того же ключа.

 func somefunction(mux *sync.Mutex, wg *sync.WaitGroup) {
    mux.Lock()
    defer mux.Unlock()
    ...
}
 
 wg := amp;sync.WaitGroup{}
wg.Add(5)
muxA := amp;sync.Mutex{}
muxB := amp;sync.Mutex{}
go somefunction(muxA, wg) //1
go somefunction(muxB, wg) //2
go somefunction(muxA, wg) //3
go somefunction(muxA, wg) //4
go somefunction(muxB, wg) //5

wg.Wait()
 

Если вы хотите продолжать использовать доступ на основе ключей, вы можете сохранить мьютексы на карте:

 muxmap := map[string]*sync.Mutex{
    "A": amp;sync.Mutex{},
    "B": amp;sync.Mutex{},
}

go somefunction(muxmap["A"], wg)
 

Ответ №2:

Вероятно, можно реализовать специальную блокировку, такую как

 type StringKeyLock struct {
    locks map[string]*sync.Mutex

    mapLock sync.Mutex // to make the map safe concurrently
}

func NewStringKeyLock() *StringKeyLock {
    return amp;StringKeyLock{locks: make(map[string]*sync.Mutex)}
}

func (l *StringKeyLock) getLockBy(key string) *sync.Mutex {
    l.mapLock.Lock()                   
    defer l.mapLock.Unlock()           
                                       
    ret, found := l.locks[key]
    if found {
        return ret
    }

    ret = amp;sync.Mutex{}
    l.locks[key] = ret
    return ret
}

func (l *StringKeyLock) Lock(key string) {
    l.getLockBy(key).Lock()
}                                                   
                                                    
func (l *StringKeyLock) Unlock(key string) {        
    l.getLockBy(key).Unlock()                       
}
 

Затем, чтобы инициализировать «глобальный» StringKeyLock

 var stringKeyLock = NewStringKeyLock()
 

Наконец, использовать его

 func somefunction(key string){
   stringKeyLock.Lock(key)
   //piece of code
   stringKeyLock.Unlock(key)
}