Метод sync.Map.CompareAndSwap

Метод sync.Map.CompareAndSwap выполняет атомарную операцию сравнения и замены значения для указанного ключа. В первый параметр мы передаем ключ, значение которого нужно проверить и заменить. Во второй параметр мы передаем ожидаемое значение, с которым сравнивается текущее значение по ключу. В третий параметр мы передаем новое значение, которое будет установлено, если сравнение прошло успешно. Метод возвращает true, если операция выполнена, и false, если текущее значение не соответствует ожидаемому.

Синтаксис

gom.CompareAndSwap(key, old, new)

Пример

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

gopackage main

import (
  "fmt"
  "sync"
)

func main() {
  var m sync.Map

  m.Store("counter", 10)

  swapped := m.CompareAndSwap("counter", 10, 20)
  fmt.Printf("swapped: %t\n", swapped)

  value, _ := m.Load("counter")
  fmt.Printf("value: %d\n", value)
}

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

go"swapped: true"
"value: 20"

Пример

Попробуем заменить значение, которое не соответствует ожидаемому:

gopackage main

import (
  "fmt"
  "sync"
)

func main() {
  var m sync.Map

  m.Store("counter", 10)

  swapped := m.CompareAndSwap("counter", 15, 20)
  fmt.Printf("swapped: %t\n", swapped)

  value, _ := m.Load("counter")
  fmt.Printf("value: %d\n", value)
}

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

go"swapped: false"
"value: 10"

Пример

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

gopackage main

import (
  "fmt"
  "sync"
)

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

  m.Store("counter", 0)

  for i := 0; i < 10; i++ {
    wg.Add(1)
    go func() {
      defer wg.Done()

      for {
        oldVal, _ := m.Load("counter")
        old := oldVal.(int)
        newVal := old + 1

        if m.CompareAndSwap("counter", old, newVal) {
          break
        }
      }
    }()
  }

  wg.Wait()

  value, _ := m.Load("counter")
  fmt.Printf("final counter: %d\n", value)
}

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

go"final counter: 10"

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

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