#go #thread-synchronization
#Вперед #синхронизация потоков
Вопрос:
У меня есть функция
type Command struct {
id Uuid
}
handleCommand(cmd Command)
{
entity := lookupEntityInDataBase(cmd.Uuid)
entity.handleCommand(cmd)
saveEntityInDatabase(entity)
}
Однако эта функция может вызываться параллельно, и предполагается, что объекты не являются потокобезопасными, что приводит к изменчивости состояния объекта и состояния, которое будет сохранено в базе данных.
Простая блокировка мьютекса в начале и в конце в этой функции решила бы эту проблему, но привела бы к чрезмерно пессимистичной синхронизации, поскольку объектам разных экземпляров (т. Е. Разным uuid) Должно быть разрешено обрабатывать их команды параллельно.
Альтернативой может быть сохранение карты map[uuid]sync.Mutex
и создание нового мьютекса, если uuid ранее не встречался, и создание его потокобезопасным способом. Однако это приведет к, возможно, бесконечно растущему отображению всех uuid, с которыми каждый сталкивается во время выполнения.
Я думал о последующей очистке мьютексов, но выполнение этого поточно-безопасного и осознание того, что другой поток, возможно, уже ожидает мьютекса, открывает так много банок с червями.
Я надеюсь, что мне не хватает очень простого и элегантного решения.
Ответ №1:
На самом деле нет элегантного решения. Вот версия, использующая каналы:
var m map[string]chan struct{}
var l sync.Mutex
func handleCommand(cmd Command) {
for {
l.Lock()
ch, ok:=m[cmd.Uuid]
if !ok {
ch=make(chan struct{})
m[uuid]=ch
defer func() {
l.Lock()
delete(m,cmd.Uuid)
close(ch)
l.Unlock()
}()
l.Unlock()
break
}
l.Unlock()
<-ch
}
entity := lookupEntityInDataBase(cmd.Uuid)
entity.handleCommand(cmd)
saveEntityInDatabase(entity)
}
Комментарии:
1. Где бы вы использовали этот цикл for? Можете ли вы уточнить ответ?
2. Я изменил ответ, чтобы включить остальные части.
Ответ №2:
В проекте Moby есть такая вещь, как библиотека, см. https://github.com/moby/locker
Однострочное описание,
locker предоставляет механизм для создания более мелкозернистой блокировки, чтобы помочь освободить более глобальные блокировки для обработки других задач.