Я не уверен, правильно ли я подхожу к этому, идея состоит в том, чтобы иметь параллелизм при чтении, но блокировать только при записи, обновлении или удалении.
type Data struct {
sync.RWMutex
fooMap map[string]Foo
}
func (d *Data) AddFoo(foo Foo) {
d.Lock()
defer d.Unlock()
d.fooMap[foo.getName()] = foo
}
func (d *Data) GetFoo(name string) Foo {
return d.fooMap[name]
}
func (d *Data) RemoveFoo(name string) Foo {
d.Lock()
defer d.Unlock()
var foo = self.fooMap[name]
if foo != nil {
delete(d.fooMap, name)
}
return foo
}
func (d *Data) HasFoo(name string) bool {
return d.fooMap[name] != nil
}
Ищу эффективный подход, при котором я не блокирую чтение из словаря, а блокирую только методы AddFoo
и RemoveFoo
.
Редактировать
Контекст немного отличается, код не будет выполняться goroutines
, а будет использоваться на сервере, на котором выполняется множество одновременных запросов.
Редактировать 2 В случае двух карт
type Data struct {
sync.RWMutex
BarMutex sync.RWMutex{}
fooMap map[string]Foo
barMap map[string]Bar
}
// for Foo
func (d *Data) AddFoo(foo Foo) {
d.Lock()
defer d.Unlock()
d.fooMap[foo.getName()] = foo
}
func (d *Data) GetFoo(name string) Foo {
return d.fooMap[name]
}
// for Bar
func (d *Data) AddBar(bar Bar) {
d.BarMutex.Lock()
defer d.BarMutex.Unlock()
d.barMap[bar.getName()] = bar
}
func (d *Data) GetBar(name string) Bar {
d.BarMutex.RLock()
defer d.BarMutex.RUnlock();
return d.barMap[name]
}
Добавление еще нескольких методов.
Однако обратите внимание, что если вы делитесь данными, вам придется блокировать некоторые чтения, если они происходят одновременно с записью. Вы не можете «заблокировать только в методах AddFoo и RemoveFoo», так не работает синхронизация.
@ Адриан, если вы можете включить свои комментарии в код в ответе, с удовольствием приму.
the code will not be executed by goroutines but will be used on a server where I've tons of concurrent requests
Если они параллельны, они находятся в горутине — например, если вы используете сервер net/http
, каждый запрос находится в своей собственной горутине. Других механизмов параллелизма не существует, поэтому, если у вас есть параллелизм, вы используете горутины, даже если ваш код не запускает их явно.
Вы не можете «блокировать только в методах AddFoo и RemoveFoo»; синхронизация работает не так. Если вы делитесь данными, вам придется получить блокировку для чтения, и вы будете блокировать некоторые операции чтения, если они происходят одновременно с записью. Если вы не получаете блокировку для чтения, у вас есть состояние гонки.
func (d *Data) GetFoo(name string) Foo {
d.RLock()
d.RUnlock()
return d.fooMap[name]
}
Альтернативой может быть делиться памятью, общаясь. Это более сложный рефакторинг, и есть много способов сделать это в зависимости от ваших конкретных потребностей; это не то, для чего я мог бы предложить решение на основе общего примера типа "Foo".
Спасибо, Адриан, я очень ценю. Я хотел бы узнать больше об этом обмене памятью, общаясь, звучит как передача мне сообщения, есть ли примерная ссылка, которой вы можете поделиться.
Это передача сообщений, и есть много способов сделать это; Я не уверен ни в одной ссылке, которая дала бы вам все разумные варианты, но я уверен, что веб-поиск выдаст огромное количество информации.
небольшой поворот, если вы не возражаете, если у меня есть две карты в type Data{}
, вторая - barMap map[string]Bar
, с собственным набором методов Get
и Add
, как мне заблокировать каждую из них по отдельности?
Используйте отдельный RWMutex
для каждой карты.
Хотя, если это ваш вариант использования, вам следует, по крайней мере, подумать о том, чтобы иметь два отдельных типа (один для хранения Foo
и один для хранения Bar
) и объединить их в комбинированный тип, если вы повсеместно используете их вместе. Вероятно, в долгосрочной перспективе с ними будет легче работать.
Ваше второе замечание верно, однако я портирую эту библиотеку, и отклонение от оригинала для меня проблема, я пытался с редактировать 2, так и должно быть? пожалуйста, просмотрите.
Кажется, это нормально, хотя вы, возможно, захотите пойти дальше и назвать оба мьютекса вместо одного именованного и одного встроенного, просто чтобы избежать путаницы.
понял, еще раз спасибо, очень ценю вашу помощь в этом.
Вы смотрели на github.com/jwells131313/Гете, у которого есть рекурсивные блокировки чтения/записи. Мы используем его для таких случаев
«код не будет выполняться горутинами, но будет использоваться на сервере, где у меня есть множество одновременных запросов», если они параллельны, они находятся в горутинах. Вам нужно добавить
d.RLock();defer d.RUnlock()
к вашимGetFoo
иHasFoo
, иначе вы рискуете гонкой данных, когда чтение происходит одновременно с записью.