Тип atomic.Bool

Тип atomic.Bool представляет собой атомарное логическое значение. В отличие от обычного bool, atomic.Bool гарантирует, что операции чтения и записи будут выполняться атомарно, что особенно важно при работе с несколькими горутинами. Основные методы типа: Load для чтения значения, Store для записи значения, Swap для обмена значения с возвратом предыдущего, CompareAndSwap для сравнения и замены значения.

Синтаксис

Для использования atomic.Bool нужно объявить переменную этого типа и использовать ее методы:

govar flag atomic.Bool
flag.Store(true)
value := flag.Load()
old := flag.Swap(false)
success := flag.CompareAndSwap(true, false)

Метод Load

Метод Load атомарно возвращает текущее значение логического флага:

gopackage main

import (
  "fmt"
  "sync/atomic"
)

func main() {
  var flag atomic.Bool
  flag.Store(true)

  res := flag.Load()
  fmt.Println(res)
}

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

gotrue

Метод Store

Метод Store атомарно устанавливает новое значение логического флага:

gopackage main

import (
  "fmt"
  "sync/atomic"
)

func main() {
  var flag atomic.Bool
  flag.Store(true)
  flag.Store(false)

  res := flag.Load()
  fmt.Println(res)
}

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

gofalse

Метод Swap

Метод Swap атомарно заменяет значение на новое и возвращает предыдущее значение:

gopackage main

import (
  "fmt"
  "sync/atomic"
)

func main() {
  var flag atomic.Bool
  flag.Store(true)

  oldValue := flag.Swap(false)
  fmt.Println(oldValue)

  newValue := flag.Load()
  fmt.Println(newValue)
}

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

gotrue
false

Метод CompareAndSwap

Метод CompareAndSwap сравнивает текущее значение с ожидаемым и, если они равны, заменяет его на новое. Возвращает true, если замена выполнена успешно:

gopackage main

import (
  "fmt"
  "sync/atomic"
)

func main() {
  var flag atomic.Bool
  flag.Store(true)

  success := flag.CompareAndSwap(true, false)
  fmt.Println(success)

  current := flag.Load()
  fmt.Println(current)
}

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

gotrue
false

А теперь пример, когда сравнение не проходит:

gopackage main

import (
  "fmt"
  "sync/atomic"
)

func main() {
  var flag atomic.Bool
  flag.Store(true)

  success := flag.CompareAndSwap(false, true)
  fmt.Println(success)

  current := flag.Load()
  fmt.Println(current)
}

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

gofalse
true

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

Давайте рассмотрим пример безопасного использования atomic.Bool в конкурентной среде:

gopackage main

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

func main() {
  var flag atomic.Bool
  var wg sync.WaitGroup

  flag.Store(false)

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

      if flag.CompareAndSwap(false, true) {
        fmt.Printf("Горутина %d захватила флаг\n", id)
        time.Sleep(100 * time.Millisecond)
        flag.Store(false)
      } else {
        fmt.Printf("Горутина %d не смогла захватить флаг\n", id)
      }
    }(i)
  }

  wg.Wait()
}

Возможный результат выполнения кода:

go"Горутина 9 захватила флаг"
"Горутина 0 не смогла захватить флаг"
"Горутина 1 не смогла захватить флаг"
"Горутина 2 не смогла захватить флаг"
"Горутина 3 не смогла захватить флаг"
"Горутина 4 не смогла захватить флаг"
"Горутина 5 не смогла захватить флаг"
"Горутина 6 не смогла захватить флаг"
"Горутина 7 не смогла захватить флаг"
"Горутина 8 не смогла захватить флаг"

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

  • тип atomic.Int32,
    который предоставляет атомарный 32-битный целочисленный тип
  • тип atomic.Int64,
    который предоставляет атомарный 64-битный целочисленный тип
  • тип atomic.Uint32,
    который предоставляет атомарный беззнаковый 32-битный целочисленный тип
  • тип atomic.Uint64,
    который предоставляет атомарный беззнаковый 64-битный целочисленный тип