Menu
CoddyTech

sync.WaitGroup

Termasuk bagian Pemrograman Berorientasi Objek dalam perjalanan GO di Coddy. Pelajaran 64 dari 107.

Dalam pelajaran Dasar Goroutine, kita menggunakan time.Sleep untuk menunggu goroutine selesai, pendekatan yang rentan. sync.WaitGroup menyediakan cara yang tepat untuk menunggu sekumpulan goroutine menyelesaikan pekerjaannya.

WaitGroup bekerja seperti penghitung. Kamu menambah nilainya sebelum memulai goroutine, menguranginya saat goroutine selesai, dan menunggu hingga penghitung mencapai nol:

func main() {
    var wg sync.WaitGroup
    
    for i := 1; i <= 3; i++ {
        wg.Add(1)  // tambah penghitung
        go func(id int) {
            defer wg.Done()  // kurangi penghitung setelah selesai
            fmt.Printf("Worker %d finished\n", id)
        }(i)
    }
    
    wg.Wait()  // tunggu hingga penghitung bernilai 0
    fmt.Println("All workers complete")
}

Tiga method utama adalah Add(n) untuk menambah penghitung sebesar n, Done() untuk menguranginya sebanyak satu (setara dengan Add(-1)), dan Wait() untuk memblokir hingga penghitung mencapai nol. Menggunakan defer wg.Done() memastikan penghitung berkurang meskipun goroutine mengalami panic.

Kesalahan umum adalah memanggil Add di dalam goroutine, bukan sebelum goroutine dimulai. Hal ini menimbulkan kondisi balapan, sehingga Wait mungkin selesai sebelum semua goroutine terdaftar:

// Salah: kondisi balapan
go func() {
    wg.Add(1)  // mungkin berjalan setelah Wait()
    defer wg.Done()
}()

// Benar: panggil Add sebelum meluncurkan fungsi
wg.Add(1)
go func() {
    defer wg.Done()
}()

WaitGroups sering disematkan dalam struct untuk mengoordinasikan operasi konkuren, sehingga penting untuk membangun tipe yang aman untuk thread di Go.

challenge icon

Tantangan

Mudah

Mari kita buat pengelola unduhan yang mengoordinasikan beberapa unduhan file secara bersamaan menggunakan sync.WaitGroup. Pengelola ini akan melacak kapan semua unduhan selesai tanpa bergantung pada timer tidur sembarang.

Kamu akan menyusun kodemu dalam dua file:

  • downloader.go: Definisikan logika koordinasi unduhanmu.

    Buat struct Download dengan field ID (int), Filename (string), dan Size (int, yang mewakili KB).

    Implementasikan method Process(wg *sync.WaitGroup) pada Download yang menyimulasikan proses unduhan dengan tidur selama durasi berdasarkan ukuran file (gunakan Size * 10 milidetik). Saat unduhan selesai, cetak: Downloaded: [Filename] ([Size]KB). Gunakan defer wg.Done() untuk memastikan penghitung WaitGroup berkurang dengan benar.

    Buat fungsi StartDownloads(downloads []Download) int yang menjalankan semua unduhan secara bersamaan menggunakan goroutine dan menunggu sampai semuanya selesai. Ingat untuk memanggil wg.Add(1) sebelum menjalankan setiap goroutine, bukan di dalamnya. Kembalikan total ukuran semua unduhan.

  • main.go: Baca informasi unduhan dan koordinasikan unduhan secara bersamaan.

    Baca jumlah unduhan, lalu untuk setiap unduhan baca ID, nama file, dan ukurannya. Buat unduhan dan teruskan ke StartDownloads. Setelah semua unduhan selesai, cetak: All downloads complete: [total]KB total

Input berikut akan diberikan:

  • Baris 1: Jumlah unduhan (integer)
  • Baris-baris berikutnya: Untuk setiap unduhan, tiga baris berisi ID (integer), nama file (string), dan ukuran dalam KB (integer)

Contohnya, jika diberikan:

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

Outputmu akan menampilkan unduhan yang selesai (file yang lebih kecil selesai lebih dulu karena waktu tidurnya lebih singkat), lalu ringkasan:

Downloaded: image.png (5KB)
Downloaded: data.csv (10KB)
Downloaded: report.pdf (20KB)
All downloads complete: 35KB total

Perbedaan utama dibandingkan dengan menggunakan time.Sleep adalah wg.Wait() memblokir hingga semua goroutine memberi sinyal bahwa prosesnya selesai dengan Done(). Dengan begitu, kamu mendapatkan sinkronisasi yang akurat, terlepas dari berapa lama waktu yang dibutuhkan setiap unduhan.

Coba sendiri

package main

import (
	"fmt"
)

func main() {
	// Baca jumlah unduhan
	var n int
	fmt.Scanln(&n)

	// Baca informasi unduhan
	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: Panggil StartDownloads dengan slice downloads
	// TODO: Cetak ringkasannya: "All downloads complete: [total]KB total"
}
quiz iconUji dirimu

Pelajaran ini berisi kuis singkat. Mulai pelajarannya untuk menjawab kuis dan melacak progresmu.

Semua pelajaran di Pemrograman Berorientasi Objek

Latihan sendiri: Compiler Go online