Атомарные счётчики в конкурентном Go

GolangGolangBeginner
Практиковаться сейчас

This tutorial is from open-source community. Access the source code

💡 Этот учебник переведен с английского с помощью ИИ. Чтобы просмотреть оригинал, вы можете перейти на английский оригинал

Введение

В этом практическом занятии мы сосредоточимся на управлении состоянием в Go с использованием пакета sync/atomic для атомарных счётчиков, доступных нескольким goroutine.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL go(("Golang")) -.-> go/ConcurrencyGroup(["Concurrency"]) go/ConcurrencyGroup -.-> go/atomic("Atomic") subgraph Lab Skills go/atomic -.-> lab-15454{{"Атомарные счётчики в конкурентном Go"}} end

Атомарные счётчики

Задача заключается в том, чтобы увеличить счётчик ровно 1000 раз с использованием 50 goroutine и пакета sync/atomic.

  • Используйте пакет sync/atomic для увеличения счётчика.
  • Используйте WaitGroup для ожидания завершения работы всех goroutine.
## Мы ожидаем получить ровно 50 000 операций. Если бы мы
## использовали неатомарный `ops++` для увеличения счётчика,
## вероятно, мы бы получили другое число, меняющееся между
## запусками, потому что goroutine будут взаимодействовать
## друг с другом. Кроме того, мы бы получили ошибки о дата-расе
## при запуске с флагом `-race`.
$ go run atomic-counters.go
ops: 50000

## Далее мы рассмотрим мьютексы, другой инструмент для управления
## состоянием.

Ниже представлен полный код:

// Основной механизм управления состоянием в Go - это
// коммуникация через каналы. Мы видели это, например,
// в [пулах рабочих потоков](worker-pools). Однако есть и
// несколько других вариантов управления состоянием. Здесь мы
// рассмотрим использование пакета `sync/atomic` для
// _атомарных счётчиков_, доступных нескольким goroutine.

package main

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

func main() {

	// Мы будем использовать незнаковое целое число для
	// представления нашего (всегда положительного) счётчика.
	var ops uint64

	// WaitGroup поможет нам дождаться завершения работы всех
	// goroutine.
	var wg sync.WaitGroup

	// Мы запустим 50 goroutine, каждая из которых увеличит
	// счётчик ровно 1000 раз.
	for i := 0; i < 50; i++ {
		wg.Add(1)

		go func() {
			for c := 0; c < 1000; c++ {
				// Чтобы атомарно увеличить счётчик, мы
				// используем `AddUint64`, передав ему адрес
				// памяти нашего счётчика `ops` с использованием
				// синтаксиса `&`.
				atomic.AddUint64(&ops, 1)
			}
			wg.Done()
		}()
	}

	// Дождитесь завершения всех goroutine.
	wg.Wait()

	// Теперь безопасно получить доступ к `ops`, потому что мы
	// знаем, что никакая другая goroutine не пишет в него.
	// Безопасное чтение атомарных переменных во время их
	// обновления также возможно с использованием функций
	// типа `atomic.LoadUint64`.
	fmt.Println("ops:", ops)
}

Резюме

В этом практическом занятии мы узнали, как использовать пакет sync/atomic для управления состоянием в Go путём увеличения счётчика с использованием нескольких goroutine. Функция AddUint64 использовалась для атомарного увеличения счётчика, а WaitGroup для ожидания завершения работы всех goroutine.