sync.WaitGroup
Coddy'nin GO Journey'sinin Nesne Yönelimli Programlama bölümünün bir parçası — ders 64 / 107.
Goroutines Temelleri dersinde, goroutinelerin bitmesini beklemek için time.Sleep kullandık; bu kırılgan bir yaklaşımdır. sync.WaitGroup, bir grup goroutinin işlerini tamamlamasını beklemek için uygun bir yol sağlar.
Bir WaitGroup bir sayaç gibi çalışır. Bir goroutine başlatmadan önce onu artırır, goroutine tamamlandığında azaltır ve sayaç sıfıra ulaşana kadar beklersiniz:
func main() {
var wg sync.WaitGroup
for i := 1; i <= 3; i++ {
wg.Add(1) // sayacı artır
go func(id int) {
defer wg.Done() // tamamlandığında azalt
fmt.Printf("Worker %d finished\n", id)
}(i)
}
wg.Wait() // sayaç 0 olana kadar bekle
fmt.Println("All workers complete")
}Üç ana yöntem şunlardır: sayacı n kadar artırmak için Add(n), sayacı bir azaltmak için Done() (Add(-1) ile eşdeğerdir) ve sayaç sıfıra ulaşana kadar engellemek için Wait(). defer wg.Done() kullanımı, goroutine paniklese bile sayacın azalmasını sağlar.
Yaygın bir hata, Add fonksiyonunu goroutine'den önce çağırmak yerine içinde çağırmaktır. Bu, tüm goroutine'ler kaydedilmeden önce Wait fonksiyonunun dönebileceği bir yarış durumuna (race condition) yol açar:
// Yanlış - race condition
go func() {
wg.Add(1) // Wait() fonksiyonundan sonra çalışabilir
defer wg.Done()
}()
// Doğru - başlatmadan önce ekleyin
wg.Add(1)
go func() {
defer wg.Done()
}()WaitGroups, eşzamanlı operasyonları koordine etmek için genellikle struct'lar içine gömülür; bu da onları Go'da thread-safe türler oluşturmak için vazgeçilmez kılar.
Görev
Kolaysync.WaitGroup kullanarak birden fazla eşzamanlı dosya indirmesini koordine eden bir indirme yöneticisi oluşturalım. Yöneticiniz, rastgele uyku zamanlayıcılarına güvenmeden tüm indirmelerin ne zaman tamamlandığını takip edecektir.
Kodunuzu iki dosya halinde düzenleyeceksiniz:
downloader.go: İndirme koordinasyon mantığınızı tanımlayın.ID(int),Filename(string) veSize(int, KB cinsinden) alanlarına sahip birDownloadyapısı (struct) oluşturun.Downloadüzerinde, dosya boyutuna göre bir süre uyuyarak (Size * 10milisaniye kullanın) indirmeyi simüle eden birProcess(wg *sync.WaitGroup)metodu uygulayın. İndirme tamamlandığında şunu yazdırın:Downloaded: [Filename] ([Size]KB). WaitGroup sayacının düzgün bir şekilde azaldığından emin olmak içindefer wg.Done()kullanın.Goroutine'leri kullanarak tüm indirmeleri eşzamanlı olarak başlatan ve hepsinin tamamlanmasını bekleyen bir
StartDownloads(downloads []Download) intfonksiyonu oluşturun. Her goroutine'i başlatmadan öncewg.Add(1)çağırmayı unutmayın, goroutine'in içinde değil. Tüm indirmelerin toplam boyutunu döndürün.main.go: İndirme bilgilerini okuyun ve eşzamanlı indirmeleri yönetin.İndirme sayısını okuyun, ardından her indirme için ID, dosya adı ve boyutunu okuyun. İndirmeleri oluşturun ve bunları
StartDownloadsfonksiyonuna iletin. Tüm indirmeler tamamlandıktan sonra şunu yazdırın:All downloads complete: [total]KB total
Aşağıdaki girdiler sağlanacaktır:
- Satır 1: İndirme sayısı (tam sayı)
- Sonraki satırlar: Her indirme için üç satır - ID (tam sayı), dosya adı (string) ve KB cinsinden boyut (tam sayı)
Örneğin, şu girdi verildiğinde:
3
1
report.pdf
20
2
image.png
5
3
data.csv
10Çıktınız, tamamlanan indirmeleri (daha kısa uyku süreleri nedeniyle küçük dosyalar önce biter) ve ardından özeti göstermelidir:
Downloaded: image.png (5KB)
Downloaded: data.csv (10KB)
Downloaded: report.pdf (20KB)
All downloads complete: 35KB totaltime.Sleep kullanmaktan temel fark, wg.Wait() fonksiyonunun tüm goroutine'ler Done() ile tamamlandığını bildirene kadar bloklanmasıdır; bu da her bir indirmenin ne kadar sürdüğünden bağımsız olarak size hassas bir senkronizasyon sağlar.
Kopya kağıdı
Bir sync.WaitGroup, goroutine'lerin tamamlanmasını beklemek için uygun bir yol sağlar. Çalışan goroutine'leri takip eden bir sayaç gibi çalışır.
Üç ana metot şunlardır:
Add(n)- sayacı n kadar artırırDone()- sayacı bir azaltır (Add(-1)ile eşdeğerdir)Wait()- sayaç sıfıra ulaşana kadar bloklar
Temel kullanım deseni:
var wg sync.WaitGroup
for i := 1; i <= 3; i++ {
wg.Add(1) // goroutine başlatılmadan önce artır
go func(id int) {
defer wg.Done() // bittiğinde azalt
fmt.Printf("Worker %d finished\n", id)
}(i)
}
wg.Wait() // tüm goroutine'ler tamamlanana kadar blokla
fmt.Println("All workers complete")defer wg.Done() kullanmak, goroutine paniklese bile sayacın azalmasını sağlar.
Önemli: Yarış durumlarını (race conditions) önlemek için Add metodunu her zaman goroutine'i başlatmadan önce çağırın, içinde değil:
// Yanlış - yarış durumu
go func() {
wg.Add(1) // Wait() metodundan sonra çalışabilir
defer wg.Done()
}()
// Doğru
wg.Add(1)
go func() {
defer wg.Done()
}()Kendin dene
package main
import (
"fmt"
)
func main() {
// İndirme sayısını oku
var n int
fmt.Scanln(&n)
// İndirme bilgilerini oku
downloads := make([]Download, n)
for i := 0; i < n; i++ {
var id int
var filename string
var size int
fmt.Scanln(&id)
fmt.Scanln(&filename)
fmt.Scanln(&size)
downloads[i] = Download{
ID: id,
Filename: filename,
Size: size,
}
}
// TODO: downloads dilimi ile StartDownloads fonksiyonunu çağır
// TODO: Özeti yazdır: "All downloads complete: [total]KB total"
}
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