Блокировки RW с операциями CRUD

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

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]
}

Добавление еще нескольких методов.

«код не будет выполняться горутинами, но будет использоваться на сервере, где у меня есть множество одновременных запросов», если они параллельны, они находятся в горутинах. Вам нужно добавить d.RLock();defer d.RUnlock() к вашим GetFoo и HasFoo, иначе вы рискуете гонкой данных, когда чтение происходит одновременно с записью.

Adrian 24.04.2019 18:48

Однако обратите внимание, что если вы делитесь данными, вам придется блокировать некоторые чтения, если они происходят одновременно с записью. Вы не можете «заблокировать только в методах AddFoo и RemoveFoo», так не работает синхронизация.

Adrian 24.04.2019 18:51

@ Адриан, если вы можете включить свои комментарии в код в ответе, с удовольствием приму.

Developer 24.04.2019 18:59
Создание API ввода вопросов на разных языках программирования (Python, PHP, Go и Node.js)
Создание API ввода вопросов на разных языках программирования (Python, PHP, Go и Node.js)
API ввода вопросов - это полезный инструмент для интеграции моделей машинного обучения, таких как ChatGPT, в приложения, требующие обработки...
1
3
77
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

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".

Спасибо, Адриан, я очень ценю. Я хотел бы узнать больше об этом обмене памятью, общаясь, звучит как передача мне сообщения, есть ли примерная ссылка, которой вы можете поделиться.

Developer 24.04.2019 19:23

Это передача сообщений, и есть много способов сделать это; Я не уверен ни в одной ссылке, которая дала бы вам все разумные варианты, но я уверен, что веб-поиск выдаст огромное количество информации.

Adrian 24.04.2019 19:25

небольшой поворот, если вы не возражаете, если у меня есть две карты в type Data{}, вторая - barMap map[string]Bar, с собственным набором методов Get и Add, как мне заблокировать каждую из них по отдельности?

Developer 24.04.2019 19:36

Используйте отдельный RWMutex для каждой карты.

Adrian 24.04.2019 20:06

Хотя, если это ваш вариант использования, вам следует, по крайней мере, подумать о том, чтобы иметь два отдельных типа (один для хранения Foo и один для хранения Bar) и объединить их в комбинированный тип, если вы повсеместно используете их вместе. Вероятно, в долгосрочной перспективе с ними будет легче работать.

Adrian 24.04.2019 20:11

Ваше второе замечание верно, однако я портирую эту библиотеку, и отклонение от оригинала для меня проблема, я пытался с редактировать 2, так и должно быть? пожалуйста, просмотрите.

Developer 24.04.2019 20:20

Кажется, это нормально, хотя вы, возможно, захотите пойти дальше и назвать оба мьютекса вместо одного именованного и одного встроенного, просто чтобы избежать путаницы.

Adrian 24.04.2019 21:02

понял, еще раз спасибо, очень ценю вашу помощь в этом.

Developer 24.04.2019 21:09

Вы смотрели на github.com/jwells131313/Гете, у которого есть рекурсивные блокировки чтения/записи. Мы используем его для таких случаев

jwells131313 25.04.2019 03:25

Другие вопросы по теме