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, goroutineleri koordine etmek için Go'nun tercih ettiği yol 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 yönteme sahiptir: Lock() ve Unlock(). Bir goroutine Lock() çağırdığında özel erişim elde eder. Lock() çağıran diğer goroutine'ler, Unlock() çağrılana kadar engellenir:
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, işlev erken dönerse veya panik oluşturursa bile kilidin serbest bırakılmasını sağlar: yaygın ve önerilen bir kalıp.
Okuma işlemleri sık ancak yazma işlemleri nadir olduğunda, sync.RWMutex daha iyi performans sunar. Birden fazla okuyucuya aynı anda izin verir, ancak yazıcılar ö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çin özel erişim
defer c.mu.Unlock()
c.data[key] = value
}Okuma işlemleri için RLock()/RUnlock(), yazma işlemleri için Lock()/Unlock() kullanın. Bu, yazma işlemlerinin güvenli olmasını sağlarken eşzamanlı okumalara olanak tanır.
Görev
KolayÜrün stok seviyelerini izleyen iş parçacığı güvenli bir envanter sistemi oluşturalım. Sisteminiz, birden çok işlem aynı anda gerçekleştiğinde veri bütünlüğünü sağlayarak mutex'leri kullanarak eşzamanlı okuma ve yazma işlemlerini güvenli bir şekilde yönetecek.
Kodunuzu iki dosya arasında düzenleyeceksiniz:
inventory.go: İş parçacığı güvenli envanter yönetim sisteminizi tanımlayın.Ürün miktarlarını bir map içinde saklayan ve erişimi korumak için
sync.RWMutexkullanan birInventorystruct'ı oluşturun. Envanteriniz şu işlemleri desteklemelidir:NewInventory() *Inventory- Başlatılmış bir map içeren yeni bir envanter oluştururAddStock(product string, quantity int)- Bir ürünün stoğuna miktar ekler (verileri değiştirdiği için özel kilit kullanın)GetStock(product string) int- Bir ürünün mevcut stok miktarını döndürür; ürün bulunamazsa 0 döndürür (yalnızca verileri okuduğu için okuma kilidi kullanın)RemoveStock(product string, quantity int) bool- Yeterli stok varsa stoktan miktar çıkarır. İşlem başarılıysatrue, stok yetersizsefalsedöndürür (özel kilit kullanın)
Kilitlerin her zaman düzgün şekilde serbest bırakılmasını sağlamak için kilitleri açarken
deferkullanmayı unutmayın.main.go: İşlemleri okuyun ve iş parçacığı güvenli envanterinizi gösterin.İşlem sayısını okuyun, ardından her işlemi işleyin. Her işlem bir tür (
add,getveyaremove), bir ürün adı veadd/removeişlemleri için bir miktar içerir.Her işlem için sonucu yazdırın:
add:Added [quantity] [product]yazdırınget:[product]: [stock] in stockyazdırınremove: İşlem başarılıysaRemoved [quantity] [product], değilseInsufficient stock for [product]yazdırın
Aşağıdaki girdiler sağlanacaktır:
- 1. satır: İş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 verildiğinde:
5
add
apples
50
get
apples
remove
apples
30
remove
apples
25
get
applesÇıktınız şu olmalıdır:
Added 50 apples
apples: 50 in stock
Removed 30 apples
Insufficient stock for apples
apples: 20 in stockRWMutex, birden çok 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.
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 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ü işle (add, get, remove)
// "add" ve "remove" için, miktarı girdiden oku
// Uygun envanter metodunu çağır
// Sonucu challenge açıklamasına göre yazdır
switch opType {
case "add":
// TODO: Miktarı oku, stok ekle, sonucu yazdır
case "get":
// TODO: Stoku 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 PoolKendi başına pratik yap: Online Go derleyicisi