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.
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.RWMutexkullanan birInventorystruct'ı oluşturun. Envanteriniz şu işlemleri desteklemelidir:NewInventory() *Inventory- Başlatılmış bir map ile yeni bir envanter oluştururAddStock(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ıysatrue, yetersiz stok varsafalsedö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
deferkullanmayı 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,getveyaremove), bir ürün adı veadd/removeişlemleri için bir miktarı vardır.Her işlem için sonucu yazdırın:
add:Added [quantity] [product]yazdırınget:[product]: [stock] in stockyazdırınremove: BaşarılıysaRemoved [quantity] [product], değilseInsufficient 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,getveyaremove) - Ürün adı
- Miktar (yalnızca
addveremoveişlemleri için)
- İşlem türü (
Ö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 stockRWMutex, 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
}
}
}
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
1Go OOP Temelleri
Harici DosyalarGo Çalışma Alanı ve ModüllerPaketler ve İçe AktarmalarDışa Aktarılan ve Aktarılmayan İsimlerGo'da OOP'ye GirişSınıf Olarak Struct'larStruct'larda Metot TanımlamaPointer ve Değer AlıcılarStruct BaşlatmaYapıcı FonksiyonlarÖzet - Basit Hesap Makinesi4Arayüzler
Arayüzlere GirişÖrtük UygulamaSözleşme Olarak ArayüzBoş Arayüz (any)Tür OnaylamaTür SeçimiArayüz BileşimiStringer ve Error ArayüzleriÖzet - Şekil Hesaplayıcı7Kapsülleme
Dışa Aktarılan ve Aktarılmayan AlanlarPaket Seviyesinde KapsüllemeGetter ve Setter MetotlarıGo'da Bilgi GizlemeÖzet - Öğrenci Kayıtları10Generics (Go 1.18+)
Generics'e GirişTür ParametreleriTür KısıtlamalarıGeneric Struct'larGeneric Metotlar İçin Geçici ÇözümÖzet - Generic Koleksiyon13Tasarım Kalıpları Bölüm 1
Tasarım Kalıplarına GirişSingleton KalıbıFactory KalıbıAbstract Factory KalıbıObserver KalıbıStrategy Kalıbı2Türler ve Struct'lar: Derinlemesine İnceleme
Temel ve Bileşik TürlerÖzel Tür TanımlamalarıStruct EtiketleriAnonim Struct'larİç İçe Geçmiş Struct'larSıfır Değerler ve VarsayılanlarÖzet - Rehber5Kalıtım Yerine Kompozisyon
Go'da Neden Kalıtım YokStruct Gömme TemelleriMetot YükseltmeBirden Fazla Struct GömmeGömme vs AgregasyonGömülü Metotları GölgelemeÖzet - Çalışan Hiyerarşisi8Hata Yönetimi ve OOP
error ArayüzüÖzel Hata TürleriHata Sarmalama (fmt.Errorf)Sentinel Hatalarerrors.Is() ve errors.As()Panic, Defer ve RecoverÖzet - Dosya Ayrıştırıcı3İşaretçiler ve Bellek
Go'da İşaretçi TemelleriStruct İşaretçileriDeğer ile Geçme ve Referans ile Geçmenew() FonksiyonuGo'da Garbage CollectionÖzet - Linked List Oluşturucu6Go'da Polimorfizm
Interface'ler ile PolimorfizmGo'da Duck TypingInterface Karşılama KurallarıPolimorfik KoleksiyonlarDependency InjectionÖzet - Payment Processor9Eşzamanlılık ve OOP
Goroutine TemelleriKanallar ve İletişimTamponlu ve Tamponsuz KanallarSelect İfadesisync.Mutex ve sync.RWMutexsync.WaitGroupThread-Safe Struct TasarımıÖzet - Worker Pool