Menu
Coddy logo textTech

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.

challenge icon

Görev

Kolay

sync.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) ve Size (int, KB cinsinden) alanlarına sahip bir Download yapısı (struct) oluşturun.

    Download üzerinde, dosya boyutuna göre bir süre uyuyarak (Size * 10 milisaniye kullanın) indirmeyi simüle eden bir Process(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çin defer wg.Done() kullanın.

    Goroutine'leri kullanarak tüm indirmeleri eşzamanlı olarak başlatan ve hepsinin tamamlanmasını bekleyen bir StartDownloads(downloads []Download) int fonksiyonu oluşturun. Her goroutine'i başlatmadan önce wg.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ı StartDownloads fonksiyonuna 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 total

time.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ır
  • Done() - 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"
}
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