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, goroutine'lerin bitirmesini beklemek için time.Sleep kullandık: kırılgan bir yaklaşım. sync.WaitGroup, bir goroutine koleksiyonunun çalışmalarını tamamlamasını beklemek için uygun bir yol sağlar.

Bir WaitGroup bir sayaç gibi çalışır. Bir goroutine başlatmadan önce sayacı artırır, goroutine tamamlandığında azaltır ve sayaç sıfıra ulaşana kadar beklersin:

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()  // bittiğinde azalt
            fmt.Printf("Worker %d finished\n", id)
        }(i)
    }
    
    wg.Wait()  // sayaç 0 olana kadar bekle
    fmt.Println("All workers complete")
}

Üç temel yöntem, 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 beklemeyi engellemek için Wait() yöntemleridir. defer wg.Done() kullanmak, goroutine paniklese bile sayacın azalmasını sağlar.

Yaygın bir hata, Add işlevini goroutine’in içinde, ondan önce değil, çağırmaktır. Bu, tüm goroutine’ler kaydedilmeden önce Wait işlevinin dönebileceği bir yarış koşulu oluşturur:

// Yanlış - yarış durumu
go func() {
    wg.Add(1)  // Wait()'den sonra çalışabilir
    defer wg.Done()
}()

// Doğru - Başlatmadan önce Add
wg.Add(1)
go func() {
    defer wg.Done()
}()

WaitGroups, eşzamanlı işlemleri koordine etmek için genellikle yapıların içine yerleştirilir ve bu da onları Go'da iş parçacığı açısından güvenli 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 edecek.

Kodunuzu iki dosyaya ayıracaksınız:

  • downloader.go: İndirme koordinasyonu mantığını tanımlayın.

    Download (int), ID (string) ve Filename (dosya boyutunu KB cinsinden temsil eden int) alanlarına sahip bir Size yapısı oluşturun.

    Process(wg *sync.WaitGroup) üzerinde, dosya boyutuna bağlı bir süre boyunca uyuyarak indirmeyi simüle eden Download metodunu uygulayın (Size * 10 milisaniyesini kullanın). İndirme tamamlandığında şunu yazdırın: Downloaded: [Filename] ([Size]KB). WaitGroup sayacının doğru şekilde azalmasını sağlamak için defer wg.Done() kullanın.

    Tüm indirmeleri goroutine'leri kullanarak eşzamanlı şekilde başlatan ve hepsinin tamamlanmasını bekleyen StartDownloads(downloads []Download) int işlevini oluşturun. Her goroutine'i başlatmadan önce, goroutine'in içinde değil, wg.Add(1) çağrısı yapmayı unutmayın. 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 kimliğini, dosya adını ve boyutunu okuyun. İndirmeleri oluşturun ve StartDownloads işlevine aktarın. Tüm indirmeler tamamlandıktan sonra şunu yazdırın: All downloads complete: [total]KB total

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

  • 1. satır: İndirme sayısı (tam sayı)
  • Sonraki satırlar: Her indirme için üç satır — kimlik (tam sayı), dosya adı (string) ve KB cinsinden boyut (tam sayı)

Örneğin, şu verildiğinde:

3
1
report.pdf
20
2
image.png
5
3
data.csv
10

Çıktınız, indirmelerin tamamlandığını (daha kısa uyku süreleri nedeniyle küçük dosyalar önce tamamlanır), 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() işlevinin tüm goroutine'ler Done() ile tamamlandıklarını bildirene kadar engellemesidir; böylece her indirmenin ne kadar sürdüğünden bağımsız olarak hassas bir eşzamanlama sağlanır.

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'ı çağır
	// TODO: Print the summary: "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

Kendi başına pratik yap: Online Go derleyicisi