Тип atomic.Uintptr

Тип atomic.Uintptr представляет собой атомарное беззнаковое целое, размер которого равен размеру указателя на данной архитектуре (например, 32 бита на 32-битных системах и 64 бита на 64-битных). Этот тип позволяет выполнять атомарные операции загрузки, сохранения, добавления, обмена и сравнения с обменом над значениями указателей. В качестве значения передается целое число, которое может быть интерпретировано как адрес памяти, а возвращаемое значение представляет собой текущее состояние счетчика или указателя. Основное назначение atomic.Uintptr — обеспечить потокобезопасную работу с указателями без использования тяжеловесных примитивов синхронизации, таких как мьютексы.

Синтаксис

govar ptr atomic.Uintptr
ptr.Store(uintptr(addr))
value := ptr.Load()
old := ptr.Swap(uintptr(newAddr))
swapped := ptr.CompareAndSwap(old, uintptr(newAddr))
ptr.Add(uintptr(delta))

Пример

Давайте создадим атомарную переменную типа atomic.Uintptr, сохраним в нее адрес переменной, а затем загрузим его обратно:

gopackage main

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

func main() {
  var p atomic.Uintptr
  x := 42
  addr := uintptr(unsafe.Pointer(&x))
  p.Store(addr)

  loadedAddr := p.Load()
  loadedX := *(*int)(unsafe.Pointer(loadedAddr))
  fmt.Println(loadedX)
}

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

go42

Пример

Теперь выполним атомарное добавление (смещение) к значению указателя. Это может быть полезно при работе с массивами или срезами:

gopackage main

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

func main() {
  var p atomic.Uintptr
  arr := []int{10, 20, 30, 40, 50}
  base := uintptr(unsafe.Pointer(&arr[0]))
  p.Store(base)

  // Смещаем указатель на размер одного элемента int
  // (на 64-битной системе это обычно 8 байт)
  p.Add(unsafe.Sizeof(arr[0]))

  newAddr := p.Load()
  val := *(*int)(unsafe.Pointer(newAddr))
  fmt.Println(val)
}

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

go20

Пример

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

gopackage main

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

func main() {
  var p atomic.Uintptr
  a := 100
  b := 200
  addrA := uintptr(unsafe.Pointer(&a))
  addrB := uintptr(unsafe.Pointer(&b))
  p.Store(addrA)

  swapped := p.CompareAndSwap(addrA, addrB)
  fmt.Println(swapped)

  loaded := p.Load()
  val := *(*int)(unsafe.Pointer(loaded))
  fmt.Println(val)
}

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

gotrue
200

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

  • тип atomic.Uint64,
    который предоставляет атомарные операции над 64-разрядными целыми
  • тип atomic.Pointer,
    который обеспечивает атомарную работу с типизированными указателями
  • функцию atomic.AddUintptr,
    которая атомарно прибавляет значение к переменной типа uintptr
  • функцию atomic.SwapPointer,
    которая атомарно обменивает значение указателя на новое