GoDasturchi
Concurrent Access Patterns

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.

example.go
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.

Hints (0/3)

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

OUTPUT

$ go run main.go
Kodingizni ishga tushiring

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.

example.go
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.

Hints (0/3)

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