Взаимное исключение в Go
Замок взаимного исключения пускает к общему счетчику только одну горутину. Остальные ждут, пока владелец не отпустит доступ; тогда следующая ветка сможет войти и безопасно изменить число.
Тип sync.Mutex хранят рядом с защищаемым
полем. Перед изменением общей переменной
вызывают Lock, после работы
Unlock. Отпуск замка удобно ставить
в отложенной функции, чтобы он сработал
и при раннем выходе из ветки.
Сто веток без замка могут потерять часть приращений:
package main
import (
"fmt"
"sync"
)
func main() {
var n int
var wg sync.WaitGroup
wg.Add(100)
for i := 0; i < 100; i++ {
go func() {
defer wg.Done()
n++
}()
}
wg.Wait()
fmt.Println(n)
}
На экране часто число меньше ста: две ветки читают одно и то же старое значение и обе пишут увеличенное на единицу.
Тот же цикл с замком дает ровно сто:
package main
import (
"fmt"
"sync"
)
func main() {
var n int
var mu sync.Mutex
var wg sync.WaitGroup
wg.Add(100)
for i := 0; i < 100; i++ {
go func() {
defer wg.Done()
mu.Lock()
defer mu.Unlock()
n++
}()
}
wg.Wait()
fmt.Println(n)
}
Дан следующий код:
var total int
var wg sync.WaitGroup
func add(wg *sync.WaitGroup) {
defer wg.Done()
total++
}
Добавьте поле замка, оберните приращение
захватом и отпуском и запустите десять
веток с ожиданием группы. Выведите
total.
Дан следующий код:
var mu sync.Mutex
var x int
func inc() {
mu.Lock()
x++
}
Скажите, чем опасен такой inc при
вызове из нескольких горутин.
Скажите, зачем Unlock в примере со
стом ветками ставят в отложенной функции
сразу после Lock.