Mutexes

Взаємні Виключення (Mutexes)|Mutexes #

// У попередньому прикладі ми розглянули як керувати простим
// лічильником за допомогою [атомарних лічильників](atomic-counters).
// У більш складних випадках ми можемо скористатись [_м’ютексами_](https://uk.wikipedia.org/wiki/М%27ютекс)
// для безпечного доступу до даних в горутинах.

package main

import (
	"fmt"
	"sync"
)

// Контейнер вміщую мапу лічильників і ми хочемо
// оновлювати їх одночасно з різних горутин.
// Ми додаємо мутекс щоб синхронізувати доступ.
// зауважте що мутекси не повинні бути скопійовані,
// тому якщо вам потрібно передавати мутекс туди-сюди
// скористайтесь вказівником.
type Container struct {
	mu       sync.Mutex
	counters map[string]int
}

func (c *Container) inc(name string) {
	// Замикаємо мутекс перед доступом до лічильників; відмикаємо
	// за допомогою відкладеного виклику перед виходом з функції.
	c.mu.Lock()
	defer c.mu.Unlock()
	c.counters[name]++
}

func main() {
	c := Container{
		// Можливо використовувати нульове значення мутекса
		// без ініціалізації.
		counters: map[string]int{"a": 0, "b": 0},
	}

	var wg sync.WaitGroup

	// Ця функція в циклі інкрементує іменний лічильник.
	doIncrement := func(name string, n int) {
		for i := 0; i < n; i++ {
			c.inc(name)
		}
		wg.Done()
	}

	// Запустимо кілька горутин одночасно;
	// Зауважте що вони мають доступ до самого `Контейнеру`,
	// та дві з них мають доступ до того самого лічильника..
	wg.Add(3)
	go doIncrement("a", 10000)
	go doIncrement("a", 10000)
	go doIncrement("b", 10000)

	// Очукуємо завершення горутин
	wg.Wait()
	fmt.Println(c.counters)
}
# Запуск прогрмми покаже що лічильники оновлені як
# і планувалось.
$ go run mutexes.go
map[a:20000 b:10000]

# Далі ми розглянемо як досягти тогож
# використовуючи лише канали та горутини.