Concurrent Access Patterns
Parallel murojaat naqshlari
Katta restoranda BITTA oshpaz emas, BIR NECHTA oshpaz bir vaqtda ishlaydi, lekin ular BITTA umumiy "buyurtmalar taxtasi"ga navbat bilan yozadi (aks holda ikkitasi bir vaqtda yozib, taxtani chalkashtirib qo'yishi mumkin). Bizning cache'imiz ham ko'plab goroutine (masalan, ko'plab HTTP so'rovlarini qayta ishlayotgan handler'lar) tomonidan BIR VAQTDA ishlatiladi — shuning uchun u THREAD-SAFE bo'lishi SHART.
package main
import (
"fmt"
"sync"
)
type Cache struct {
mu sync.RWMutex
data map[string]string
}
func NewCache() *Cache {
return &Cache{data: make(map[string]string)}
}
func (c *Cache) Set(key, value string) {
c.mu.Lock()
defer c.mu.Unlock()
c.data[key] = value
}
func (c *Cache) Get(key string) (string, bool) {
c.mu.RLock()
defer c.mu.RUnlock()
value, ok := c.data[key]
return value, ok
}
func main() {
cache := NewCache()
var wg sync.WaitGroup
for i := 0; i < 100; i++ {
wg.Add(1)
go func(n int) {
defer wg.Done()
cache.Set(fmt.Sprintf("key%d", n), fmt.Sprintf("value%d", n))
}(i)
}
wg.Wait()
fmt.Println(len(cache.data))
}100 ta goroutine BIR VAQTDA cache.Setni chaqiradi — agar sync.RWMutex bo'lmasa, bu race condition ("Race Conditions & the Race Detector" darsini eslang) keltirib chiqarardi: bir nechta goroutine bir vaqtda xaritaga yozganda, Go DASTURI HATTO XAVFLI TARZDA BUZILISHI (crash) mumkin. mu.Lock()/mu.Unlock() bu yozishlarni NAVBAT bilan, xavfsiz tartibda amalga oshirishni kafolatlaydi.
Yuqori yukli tizimlarda BITTA RWMutex ham "tirbandlik" (bottleneck) bo'lishi mumkin — production Redis/memcached klonlari ko'pincha sharding (kesh ma'lumotini bir nechta kichik, MUSTAQIL qulflangan bo'lakka bo'lish) orqali bu muammoni yechadi, chunki turli "bo'lak"larga yozish PARALLEL, bir-biriga to'sqinlik qilmasdan bajarilishi mumkin.
>_ Exercise
Cache'ga xavfsiz "increment" (sonni oshirish) metodini qo'shing.
- •Increment(key string) metodini yozing: mu.Lock() bilan himoyalab, kalit mavjud bo'lmasa "0"dan boshlab, mavjud qiymatni int'ga aylantirib 1 qo'shing va qayta string qilib saqlang (strconv paketidan foydalaning)
- •50 ta goroutine orqali bitta "counter" kalitini PARALLEL Increment qiling (sync.WaitGroup bilan)
- •Yakuniy qiymatni Get orqali olib, chop eting
Stuck? Reveal a hint to help you.
Key Takeaway
Key Takeaway:
O'qish-hisoblash-yozish (read-modify-write) operatsiyasini BUTUNLIGICHA bitta qulf ichida bajarish kerak — aks holda bir nechta goroutine bir vaqtda ishlaganda, ba'zi yangilanishlar 'yo'qolib' ketishi mumkin.
NEXT UP
Persistence Layer
$ go run main.go
Kodingizni ishga tushiring