Menu
Coddy logo textTech

sync.Mutex ve sync.RWMutex

Coddy'nin GO Journey'sinin Nesne Yönelimli Programlama bölümünün bir parçası — ders 63 / 107.

Kanallar Go'nun goroutine'leri koordine etmek için tercih ettiği yöntem olsa da, bazen paylaşılan verileri doğrudan korumanız gerekir. sync paketi, bir kaynağa aynı anda yalnızca bir goroutine'in erişmesini sağlayan kilitler olan mutex'ler sunar.

Bir sync.Mutex iki metoda sahiptir: Lock() ve Unlock(). Bir goroutine Lock() metodunu çağırdığında, özel erişim elde eder. Lock() metodunu çağıran diğer goroutine'ler, Unlock() çağrılana kadar bloklanacaktır:

type Counter struct {
    mu    sync.Mutex
    value int
}

func (c *Counter) Increment() {
    c.mu.Lock()
    c.value++
    c.mu.Unlock()
}

func (c *Counter) Value() int {
    c.mu.Lock()
    defer c.mu.Unlock()
    return c.value
}

defer c.mu.Unlock() kullanmak, fonksiyon erken dönse veya paniklese bile kilidin serbest bırakılmasını sağlar—bu yaygın ve önerilen bir kalıptır.

Okumalar sık ancak yazmalar nadir olduğunda, sync.RWMutex daha iyi performans sunar. Aynı anda birden fazla okuyucuya izin verir, ancak yazarlar özel erişim elde eder:

type Cache struct {
    mu   sync.RWMutex
    data map[string]string
}

func (c *Cache) Get(key string) string {
    c.mu.RLock()         // birden fazla okuyucuya izin verilir
    defer c.mu.RUnlock()
    return c.data[key]
}

func (c *Cache) Set(key, value string) {
    c.mu.Lock()          // yazma işlemi için özel erişim
    defer c.mu.Unlock()
    c.data[key] = value
}

Okuma işlemleri için RLock()/RUnlock() ve yazma işlemleri için Lock()/Unlock() kullanın. Bu, yazma işlemlerinin güvenli olmasını sağlarken eşzamanlı okumalara izin verir.

challenge icon

Görev

Kolay

Ürün stok seviyelerini takip eden, iş parçacığı açısından güvenli (thread-safe) bir envanter sistemi oluşturalım. Sisteminiz, birden fazla işlem aynı anda gerçekleştiğinde veri bütünlüğünü sağlamak için mutex'leri kullanarak eşzamanlı okuma ve yazma işlemlerini güvenli bir şekilde yönetecektir.

Kodunuzu iki dosya halinde düzenleyeceksiniz:

  • inventory.go: İş parçacığı açısından güvenli envanter yönetim sisteminizi tanımlayın.

    Ürün miktarlarını bir map içinde saklayan ve erişimi korumak için bir sync.RWMutex kullanan bir Inventory struct'ı oluşturun. Envanteriniz şu işlemleri desteklemelidir:

    • NewInventory() *Inventory - Başlatılmış bir map ile yeni bir envanter oluşturur
    • AddStock(product string, quantity int) - Bir ürünün stoğuna miktar ekler (veriyi değiştirdiği için özel kilit - exclusive lock - kullanın)
    • GetStock(product string) int - Bir ürün için mevcut stoğu döndürür, bulunamazsa 0 döndürür (yalnızca veri okuduğu için okuma kilidi - read lock - kullanın)
    • RemoveStock(product string, quantity int) bool - Yeterli stok varsa stoktan miktar çıkarır. Başarılıysa true, yetersiz stok varsa false döndürür (özel kilit kullanın)

    Kilitlerin her zaman düzgün bir şekilde serbest bırakılmasını sağlamak için kilit açma (unlocking) işlemlerinde defer kullanmayı unutmayın.

  • main.go: İşlemleri okuyun ve iş parçacığı açısından güvenli envanterinizi gösterin.

    İşlem sayısını okuyun, ardından her bir işlemi işleyin. Her işlemin bir türü (add, get veya remove), bir ürün adı ve add/remove işlemleri için bir miktarı vardır.

    Her işlem için sonucu yazdırın:

    • add: Added [quantity] [product] yazdırın
    • get: [product]: [stock] in stock yazdırın
    • remove: Başarılıysa Removed [quantity] [product], değilse Insufficient stock for [product] yazdırın

Aşağıdaki girdiler sağlanacaktır:

  • Satır 1: İşlem sayısı (tam sayı)
  • Sonraki satırlar: Her işlem için:
    • İşlem türü (add, get veya remove)
    • Ürün adı
    • Miktar (yalnızca add ve remove işlemleri için)

Örneğin, şu girdi verildiğinde:

5
add
apples
50
get
apples
remove
apples
30
remove
apples
25
get
apples

Çıktınız şu şekilde olmalıdır:

Added 50 apples
apples: 50 in stock
Removed 30 apples
Insufficient stock for apples
apples: 20 in stock

RWMutex, birden fazla GetStock çağrısının aynı anda okuma yapmasına izin verirken, AddStock ve RemoveStock envanteri değiştirirken özel erişim elde eder.

Kopya kağıdı

sync paketi, birden fazla goroutine'in aynı kaynağa doğrudan erişmesi gerektiğinde paylaşılan verileri korumak için mutex'ler sağlar.

sync.Mutex

Bir sync.Mutex, Lock() ve Unlock() kullanarak özel erişim sağlar:

type Counter struct {
    mu    sync.Mutex
    value int
}

func (c *Counter) Increment() {
    c.mu.Lock()
    c.value++
    c.mu.Unlock()
}

func (c *Counter) Value() int {
    c.mu.Lock()
    defer c.mu.Unlock()
    return c.value
}

Unlock() ile defer kullanmak, fonksiyon erken dönse veya paniklese bile kilidin serbest bırakılmasını sağlar.

sync.RWMutex

Bir sync.RWMutex, birden fazla eşzamanlı okuyucuya izin verir ancak yazıcılar için özel erişim sağlar:

type Cache struct {
    mu   sync.RWMutex
    data map[string]string
}

func (c *Cache) Get(key string) string {
    c.mu.RLock()         // birden fazla okuyucuya izin verilir
    defer c.mu.RUnlock()
    return c.data[key]
}

func (c *Cache) Set(key, value string) {
    c.mu.Lock()          // yazma için özel erişim
    defer c.mu.Unlock()
    c.data[key] = value
}

Okuma işlemleri için RLock()/RUnlock() ve yazma işlemleri için Lock()/Unlock() kullanın.

Kendin dene

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	reader := bufio.NewReader(os.Stdin)
	
	// İşlem sayısını oku
	line, _ := reader.ReadString('\n')
	numOps, _ := strconv.Atoi(strings.TrimSpace(line))
	
	// Yeni bir envanter oluştur
	inventory := NewInventory()
	
	// Her bir işlemi işle
	for i := 0; i < numOps; i++ {
		// İşlem türünü oku
		opLine, _ := reader.ReadString('\n')
		opType := strings.TrimSpace(opLine)
		
		// Ürün adını oku
		productLine, _ := reader.ReadString('\n')
		product := strings.TrimSpace(productLine)
		
		// TODO: Her işlem türünü ele al (add, get, remove)
		// "add" ve "remove" için girdiden miktarı oku
		// Uygun envanter metodunu çağır
		// Sonucu meydan okuma açıklamasına göre yazdır
		
		switch opType {
		case "add":
			// TODO: Miktarı oku, stok ekle, sonucu yazdır
			
		case "get":
			// TODO: Stoğu al, sonucu yazdır
			
		case "remove":
			// TODO: Miktarı oku, stok çıkar, uygun sonucu yazdır
			
		}
	}
}
quiz iconKendini test et

Bu ders kısa bir quiz içerir. Soruları yanıtlamak ve ilerlemeni kaydetmek için derse başla.

Nesne Yönelimli Programlama bölümündeki tüm dersler