ограниченные «пулы мьютексов» для синхронизированного поведения объектов

#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 предоставляет механизм для создания более мелкозернистой блокировки, чтобы помочь освободить более глобальные блокировки для обработки других задач.