Метод sync.Map.LoadOrStore

Метод LoadOrStore структуры sync.Map атомарно выполняет операцию получения значения по ключу или его сохранения, если ключ отсутствует. Первым параметром метод принимает ключ, вторым — значение для сохранения. Метод возвращает два значения: фактическое значение, связанное с ключом (загруженное или только что сохраненное), и булев флаг true, если значение было загружено из карты, или false, если значение было сохранено. Этот метод гарантирует, что операция чтения или записи выполняется атомарно и безопасна для конкурентного использования.

Синтаксис

goval, loaded := m.LoadOrStore(key, value)

Пример

Давайте попробуем загрузить значение по ключу, который отсутствует в карте:

gopackage main

import (
  "fmt"
  "sync"
)

func main() {
  var m sync.Map

  val, loaded := m.LoadOrStore("key1", "hello")
  fmt.Printf("val: %v, loaded: %v\n", val, loaded)
}

Результат выполнения кода:

go"val: hello, loaded: false"

Так как ключа "key1" нет в карте, метод сохранил значение "hello" и вернул его, а флаг loaded равен false.

Пример

Теперь попробуем загрузить значение по ключу, который уже существует в карте:

gopackage main

import (
  "fmt"
  "sync"
)

func main() {
  var m sync.Map
  m.Store("key2", 42)

  val, loaded := m.LoadOrStore("key2", 100)
  fmt.Printf("val: %v, loaded: %v\n", val, loaded)
}

Результат выполнения кода:

go"val: 42, loaded: true"

Поскольку ключ "key2" уже есть в карте, метод вернул существующее значение 42 и флаг loaded равен true, а переданное значение 100 было проигнорировано.

Пример

Пример использования метода в конкурентной среде с несколькими горутинами:

gopackage main

import (
  "fmt"
  "sync"
)

func main() {
  var m sync.Map
  var wg sync.WaitGroup

  for i := 0; i < 10; i++ {
    wg.Add(1)
    go func(i int) {
      defer wg.Done()
      val, loaded := m.LoadOrStore("shared", i)
      fmt.Printf("goroutine %d: val=%v, loaded=%v\n", i, val, loaded)
    }(i)
  }

  wg.Wait()
}

Результат выполнения кода (порядок может отличаться):

go"goroutine 9: val=0, loaded=true"
"goroutine 7: val=0, loaded=true"
"goroutine 5: val=0, loaded=true"
"goroutine 3: val=0, loaded=true"
"goroutine 1: val=0, loaded=true"
"goroutine 8: val=0, loaded=true"
"goroutine 6: val=0, loaded=true"
"goroutine 4: val=0, loaded=true"
"goroutine 2: val=0, loaded=true"
"goroutine 0: val=0, loaded=false"

В этом примере только первая горутина, которая успела вызвать LoadOrStore, сохранит значение (флаг loaded будет false). Все остальные горутины получат уже сохраненное значение и флаг loaded равный true, что гарантирует, что значение будет сохранено только один раз.

Смотрите также

  • метод sync.Map.Store,
    который просто сохраняет значение по ключу
  • метод sync.Map.Load,
    который загружает значение по ключу
  • метод sync.Map.LoadAndDelete,
    который загружает и удаляет значение по ключу
  • метод sync.Map.Swap,
    который атомарно заменяет значение по ключу