Go by Example: Mutexes

Oldingi misolda biz atomik amallar yordamida oddiy hisoblagich holatini qanday boshqarishni ko’rdik. Murakkabroq holat uchun bir nechta goroutina orasida ma’lumotlarga xavfsiz kirish uchun mutex dan foydalanishimiz mumkin.

package main
import (
    "fmt"
    "sync"
)

Container hisoblagichlar map ini saqlaydi; biz uni bir nechta goroutinadan parallel yangilamoqchi bo’lganimiz uchun kirishni sinxronlash maqsadida Mutex qo’shamiz. E’tibor bering, mutexlar nusxalanmasligi kerak, shuning uchun bu struct uzatilsa, u ko’rsatkich orqali bajarilishi kerak.

type Container struct {
    mu       sync.Mutex
    counters map[string]int
}

counters ga kirishdan oldin mutex ni bloklang; uni funksiya oxirida defer operatori yordamida blokdan chiqaring.

func (c *Container) inc(name string) {
    c.mu.Lock()
    defer c.mu.Unlock()
    c.counters[name]++
}

E’tibor bering, mutex ning nol qiymati o’zicha ishlatsa bo’ladi, shuning uchun bu yerda ishga tushirish talab etilmaydi.

func main() {
    c := Container{
        counters: map[string]int{"a": 0, "b": 0},
    }
    var wg sync.WaitGroup

Bu funksiya nomlangan hisoblagichni siklda oshiradi.

    doIncrement := func(name string, n int) {
        for range n {
            c.inc(name)
        }
    }

Bir nechta goroutinani parallel ishga tushiramiz; e’tibor bering, ularning barchasi bir xil Container ga kiradi, va ulardan ikkitasi bir xil hisoblagichga kiradi.

    wg.Go(func() {
        doIncrement("a", 10000)
    })
    wg.Go(func() {
        doIncrement("a", 10000)
    })
    wg.Go(func() {
        doIncrement("b", 10000)
    })

Goroutinalar tugashini kutamiz

    wg.Wait()
    fmt.Println(c.counters)
}

Dasturni ishga tushirish hisoblagichlar kutilganidek yangilanganini ko’rsatadi.

$ go run mutexes.go
map[a:20000 b:10000]

Keyingi navbatda biz xuddi shu holatni boshqarish vazifasini faqat goroutinalar va kanallar yordamida amalga oshirishni ko’rib chiqamiz.

Keyingi misol: .