Функция atomic.CompareAndSwapPointer
Функция atomic.CompareAndSwapPointer атомарно сравнивает значение указателя с ожидаемым значением и, если они равны, заменяет его на новое значение. Операция выполняется атомарно, что гарантирует корректность в многопоточных программах. В первый параметр мы передаем указатель на значение типа unsafe.Pointer, которое нужно сравнить и обновить, во второй параметр передаем ожидаемое значение указателя, в третий параметр передаем новое значение, на которое нужно заменить указатель при успешном сравнении.
Функция возвращает true, если операция обмена прошла успешно, и false, если ожидаемое значение не совпало с текущим. Она полезна для реализации безопасных алгоритмов без блокировок, например, при работе с односвязными списками или для управления состоянием объектов в конкурентной среде.
Важно помнить, что функция работает с типом unsafe.Pointer, поэтому для работы с конкретными типами указателей необходимо выполнять преобразование через unsafe.Pointer. Также следует соблюдать осторожность при работе с этой функцией, так как она обходит систему типов Go.
Синтаксис
atomic.CompareAndSwapPointer(addr *unsafe.Pointer, old, new unsafe.Pointer) bool
Пример
Давайте рассмотрим базовый пример использования функции для атомарной замены указателя:
package main
import (
"fmt"
"sync/atomic"
"unsafe"
)
func main() {
var ptr unsafe.Pointer
oldValue := "old"
newValue := "new"
atomic.CompareAndSwapPointer(&ptr, nil, unsafe.Pointer(&oldValue))
fmt.Printf("ptr: %v\n", *(*string)(ptr))
swapped := atomic.CompareAndSwapPointer(&ptr, unsafe.Pointer(&oldValue), unsafe.Pointer(&newValue))
fmt.Printf("swapped: %v, ptr: %v\n", swapped, *(*string)(ptr))
}
Результат выполнения кода:
"ptr: old"
"swapped: true, ptr: new"
Пример
Давайте рассмотрим пример, где операция обмена не выполняется из-за несовпадения ожидаемого значения:
package main
import (
"fmt"
"sync/atomic"
"unsafe"
)
func main() {
val := "initial"
var ptr unsafe.Pointer = unsafe.Pointer(&val)
expected := "wrong"
newVal := "updated"
swapped := atomic.CompareAndSwapPointer(&ptr, unsafe.Pointer(&expected), unsafe.Pointer(&newVal))
fmt.Printf("swapped: %v, ptr: %v\n", swapped, *(*string)(ptr))
}
Результат выполнения кода:
"swapped: false, ptr: initial"
Пример
Рассмотрим практический пример использования функции для реализации безопасного обновления конфигурации в конкурентной среде:
package main
import (
"fmt"
"sync"
"sync/atomic"
"unsafe"
)
type Config struct {
Name string
Port int
}
var configPtr unsafe.Pointer
func updateConfig(newConfig *Config) bool {
oldPtr := atomic.LoadPointer(&configPtr)
return atomic.CompareAndSwapPointer(&configPtr, oldPtr, unsafe.Pointer(newConfig))
}
func getConfig() *Config {
return (*Config)(atomic.LoadPointer(&configPtr))
}
func main() {
initial := &Config{Name: "default", Port: 8080}
atomic.StorePointer(&configPtr, unsafe.Pointer(initial))
var wg sync.WaitGroup
for i := 0; i < 100; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
newConfig := &Config{Name: fmt.Sprintf("config-%d", id), Port: 8000 + id}
if updateConfig(newConfig) {
fmt.Printf("goroutine %d: updated to %s\n", id, getConfig().Name)
}
}(i)
}
wg.Wait()
finalConfig := getConfig()
fmt.Printf("final config: %s:%d\n", finalConfig.Name, finalConfig.Port)
}
Результат выполнения кода:
"goroutine 59: updated to config-59"
"goroutine 80: updated to config-80"
"goroutine 43: updated to config-43"
"goroutine 0: updated to config-0"
"goroutine 76: updated to config-76"
"goroutine 93: updated to config-93"
"goroutine 29: updated to config-29"
"goroutine 92: updated to config-92"
"goroutine 61: updated to config-61"
"goroutine 56: updated to config-56"
"goroutine 8: updated to config-8"
"goroutine 74: updated to config-74"
"goroutine 9: updated to config-9"
"goroutine 12: updated to config-12"
"goroutine 51: updated to config-51"
"goroutine 47: updated to config-47"
"goroutine 35: updated to config-35"
"goroutine 73: updated to config-73"
"goroutine 70: updated to config-70"
"goroutine 65: updated to config-65"
"goroutine 55: updated to config-55"
"goroutine 60: updated to config-60"
"goroutine 44: updated to config-44"
"goroutine 30: updated to config-30"
"goroutine 14: updated to config-14"
"goroutine 7: updated to config-7"
"goroutine 27: updated to config-27"
"goroutine 10: updated to config-10"
"goroutine 72: updated to config-72"
"goroutine 68: updated to config-68"
"goroutine 36: updated to config-36"
"goroutine 13: updated to config-13"
"goroutine 63: updated to config-63"
"goroutine 42: updated to config-42"
"goroutine 69: updated to config-69"
"goroutine 34: updated to config-34"
"goroutine 53: updated to config-53"
"goroutine 87: updated to config-87"
"goroutine 81: updated to config-81"
"goroutine 2: updated to config-2"
"goroutine 19: updated to config-19"
"goroutine 67: updated to config-67"
"goroutine 25: updated to config-25"
"goroutine 23: updated to config-23"
"goroutine 22: updated to config-22"
"goroutine 28: updated to config-28"
"goroutine 32: updated to config-32"
"goroutine 50: updated to config-50"
"goroutine 91: updated to config-91"
"goroutine 64: updated to config-64"
"goroutine 85: updated to config-85"
"goroutine 33: updated to config-33"
"goroutine 40: updated to config-40"
"goroutine 54: updated to config-54"
"goroutine 48: updated to config-48"
"goroutine 24: updated to config-24"
"goroutine 11: updated to config-11"
"goroutine 52: updated to config-52"
"goroutine 66: updated to config-66"
"goroutine 17: updated to config-17"
"goroutine 16: updated to config-16"
"goroutine 89: updated to config-89"
"goroutine 88: updated to config-88"
"goroutine 71: updated to config-71"
"goroutine 45: updated to config-45"
"goroutine 83: updated to config-83"
"goroutine 77: updated to config-77"
"goroutine 37: updated to config-37"
"goroutine 18: updated to config-18"
"goroutine 86: updated to config-86"
"goroutine 94: updated to config-94"
"goroutine 20: updated to config-20"
"goroutine 21: updated to config-21"
"goroutine 62: updated to config-62"
"goroutine 38: updated to config-38"
"goroutine 82: updated to config-82"
"goroutine 39: updated to config-39"
"goroutine 96: updated to config-96"
"goroutine 41: updated to config-41"
"goroutine 5: updated to config-5"
"goroutine 49: updated to config-49"
"goroutine 26: updated to config-26"
"goroutine 97: updated to config-97"
"goroutine 98: updated to config-98"
"goroutine 99: updated to config-99"
"goroutine 75: updated to config-75"
"goroutine 1: updated to config-1"
"goroutine 31: updated to config-31"
"goroutine 46: updated to config-46"
"goroutine 78: updated to config-78"
"goroutine 4: updated to config-4"
"goroutine 3: updated to config-3"
"goroutine 84: updated to config-84"
"goroutine 79: updated to config-79"
"goroutine 58: updated to config-58"
"goroutine 57: updated to config-57"
"goroutine 95: updated to config-95"
"goroutine 90: updated to config-90"
"goroutine 6: updated to config-6"
"goroutine 15: updated to config-15"
"final config: config-15:8015"
Смотрите также
-
функцию
atomic.CompareAndSwapInt32,
которая выполняет атомарное сравнение и обмен для 32-битного целого числа -
функцию
atomic.CompareAndSwapInt64,
которая выполняет атомарное сравнение и обмен для 64-битного целого числа -
функцию
atomic.SwapPointer,
которая атомарно заменяет указатель на новое значение без сравнения -
функцию
atomic.LoadPointer,
которая атомарно загружает значение указателя