Пока готовил материал для новых постов, понял что не рассказал о задачке с которой иногда приходится иметь дело в работе.
Представьте что у нас конкурентное приложение. И в нем есть объект обязанный существовать в единственном экземпляре. С ним и будут работать наши потоки. Инициализация объекта тяжелая, занимает какое то время.
Варианты:
- Инициализация ресурса на старте.
- Инициализация в момент запроса ресурса (lazy init).
Вы с командой подумали и решили - на старте слишком долго, давайте делать лениво. Посидели, подумали и получилось вот так:
type Cache struct {
data map[string]string
}
type Service struct {
cache *Cache
mu sync.Mutex
}
func (s *Service) GetCache() *Cache {
s.mu.Lock()
defer s.mu.Unlock()
if s.cache == nil {
s.cache = &Cache{
data: make(map[string]string),
}
}
return s.cache
}Всё четко работает, но после релиза видите по метрикам и профилировщику что горутины начали конкурировать в этом кусочке кода (он вызывается часто). Надо что-то менять, и вы приходите к выводу - мьютекс нужен только когда объект не существует, иначе нужно просто вернуть ресурс.
package main
// код выше не изменился
func (s *Service) GetCache() *Cache {
if s.cache == nil {
s.mu.Lock()
defer s.mu.Unlock()
if s.cache == nil {
s.cache = &Cache{
data: make(map[string]string),
}
}
}
return s.cache
}
Все работает четко, но код слегка попахивает. Можно ли красивее?
// код выше не изменился
type Service struct {
cache *Cache
once sync.Once
}
func (s *Service) GetCache() *Cache {
s.once.Do(func() {
s.cache = &Cache{
data: make(map[string]string),
}
})
return s.cache
}
Это и есть Double Checked Locking (DCL). На первый взгляд может показаться что проблема высосана из пальца, но на самом деле Go просто обошелся малой кровью - спасибо за простую модель памяти. В Java раньше был целый челлендж с тем чтобы правильно написать подобный код - почитать об этом можно здесь и здесь. Вторая ссылка так вообще монументальная, под ней подписался в том числе Joshua Bloch - автор Effective Java. В его книге как раз есть глава посвященная этой проблеме. Если на моем канале есть джависты, ставьте 🐳 и рассказывайте как вы писали свой первый синглтон😁
Приходилось ли вам писать подобный код и к чему это привело?