Тип atomic.Pointer

Тип atomic.Pointer из пакета sync/atomic предоставляет атомарный указатель на значение любого типа. Он позволяет безопасно читать и изменять указатель из нескольких горутин без использования блокировок. Основные методы типа: Load для атомарного чтения, Store для атомарной записи, Swap для атомарной замены с возвратом старого значения и CompareAndSwap для условной атомарной замены.

Синтаксис

govar ptr atomic.Pointer[T]
ptr.Store(value)
value := ptr.Load()
old := ptr.Swap(new)
swapped := ptr.CompareAndSwap(old, new)

Пример с Load и Store

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

gopackage main

import (
  "fmt"
  "sync/atomic"
)

func main() {
  var ptr atomic.Pointer[string]

  // Store value
  initial := "hello"
  ptr.Store(&initial)

  // Load value
  value := ptr.Load()
  fmt.Println(*value)

  // Store new value
  new := "world"
  ptr.Store(&new)

  // Load new value
  value = ptr.Load()
  fmt.Println(*value)
}

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

go"hello"
"world"

Пример со Swap

Метод Swap атомарно заменяет указатель на новое значение и возвращает старое:

gopackage main

import (
  "fmt"
  "sync/atomic"
)

func main() {
  var ptr atomic.Pointer[string]

  first := "first"
  ptr.Store(&first)

  // Swap old value with new
  second := "second"
  old := ptr.Swap(&second)

  fmt.Println("Old value:", *old)
  fmt.Println("New value:", *ptr.Load())
}

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

go"Old value: first"
"New value: second"

Пример с CompareAndSwap

Метод CompareAndSwap выполняет атомарную замену только если текущее значение указателя совпадает с ожидаемым:

gopackage main

import (
  "fmt"
  "sync/atomic"
)

func main() {
  var ptr atomic.Pointer[string]

  current := "current"
  ptr.Store(¤t)

  expected := "current"
  new := "updated"

  // This will succeed
  swapped := ptr.CompareAndSwap(&expected, &new)
  fmt.Println("Swapped:", swapped)
  fmt.Println("Value:", *ptr.Load())

  // This will fail
  expected = "wrong"
  new = "another"
  swapped = ptr.CompareAndSwap(&expected, &new)
  fmt.Println("Swapped:", swapped)
  fmt.Println("Value:", *ptr.Load())
}

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

go"Swapped: true"
"Value: updated"
"Swapped: false"
"Value: updated"

Пример с использованием в горутинах

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

gopackage main

import (
  "fmt"
  "sync"
  "sync/atomic"
  "time"
)

func main() {
  var ptr atomic.Pointer[string]
  var wg sync.WaitGroup

  initial := "initial"
  ptr.Store(&initial)

  // Writer goroutine
  wg.Add(1)
  go func() {
    defer wg.Done()
    time.Sleep(100 * time.Millisecond)
    updated := "updated"
    ptr.Store(&updated)
    fmt.Println("Writer: stored new value")
  }()

  // Reader goroutines
  for i := 0; i < 3; i++ {
    wg.Add(1)
    go func(id int) {
      defer wg.Done()
      time.Sleep(50 * time.Millisecond)
      value := ptr.Load()
      fmt.Printf("Reader %d: %s\n", id, *value)
    }(i)
  }

  wg.Wait()
}

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

go"Reader 2: initial"
"Reader 0: initial"
"Reader 1: initial"
"Writer: stored new value"

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

  • тип atomic.Value,
    который предоставляет атомарное хранилище для значений любого типа
  • тип atomic.Bool,
    который предоставляет атомарный булевый тип
  • тип sync.Mutex,
    который обеспечивает взаимное исключение для защиты данных
  • пакет go,
    который используется для запуска горутин в Go