Site icon JocoDEV

Memahami Goroutine dan Slice dalam Bahasa Go

Bahasa Go dikenal karena kesederhanaannya, tetapi dua konsep intinya sering membuat pemula bingung: goroutine dan slice. Goroutine adalah cara Go menjalankan banyak pekerjaan secara bersamaan tanpa harus mengelola thread secara manual, sedangkan slice adalah struktur data dinamis yang dipakai hampir di setiap program Go. Memahami keduanya adalah kunci untuk menulis kode Go yang cepat, aman, dan mudah dirawat.

Bagi developer yang baru pindah dari bahasa lain, pertanyaan yang paling sering muncul adalah bagaimana goroutine bekerja di balik layar dan mengapa slice bisa berubah perilakunya saat ditambahkan elemen. Artikel ini menjawab kedua hal tersebut secara bertahap, mulai dari konsep dasar hingga jebakan yang perlu dihindari saat menggabungkan keduanya.

Apa Itu Goroutine dan Kenapa Penting

Goroutine adalah unit eksekusi ringan yang dikelola langsung oleh runtime Go, bukan oleh sistem operasi. Satu goroutine hanya membutuhkan beberapa kilobyte memori, jauh lebih kecil dibanding thread OS yang bisa memakan beberapa megabyte. Karena itu, sebuah program Go dapat menjalankan ribuan bahkan jutaan goroutine sekaligus tanpa membebani mesin secara berlebihan.

Model konkurensi ini membuat Go sangat cocok untuk pekerjaan backend seperti menangani banyak koneksi jaringan, memproses antrean tugas, atau memanggil beberapa API secara paralel. Jika Anda ingin memperdalam dasar-dasar bahasa ini, dokumentasi resmi di halaman dokumentasi bahasa Go adalah tempat terbaik untuk mulai membaca spesifikasi dan panduan resminya.

Cara Menjalankan Goroutine

Untuk menjalankan sebuah fungsi sebagai goroutine, cukup tambahkan kata kunci `go` di depannya:

go kirimEmail(pengguna)

Fungsi tersebut akan berjalan di latar belakang sementara program utama melanjutkan baris berikutnya. Karena itu, goroutine yang tidak disinkronkan bisa selesai setelah program utama berhenti, sehingga hasilnya tidak pernah terlihat.

Sinkronisasi dengan WaitGroup dan Channel

Ada dua alat utama untuk menyinkronkan goroutine. Pertama, `sync.WaitGroup` yang memungkinkan program utama menunggu sekumpulan goroutine selesai. Kedua, channel, yaitu saluran berjenis yang dipakai untuk mengirim data antar goroutine secara aman. Channel mencegah akses data bersamaan yang bisa memicu race condition.

Memahami Slice di Go

Slice adalah tipe data yang merepresentasikan potongan dari sebuah array. Berbeda dengan array yang panjangnya tetap, slice bisa bertambah dan berkurang secara dinamis. Inilah alasan slice menjadi pilihan default saat developer Go perlu menyimpan daftar nilai.

Perbedaan Array dan Slice

Array memiliki panjang yang ditentukan saat deklarasi dan tidak bisa berubah, misalnya `[5]int`. Slice tidak menyebutkan panjangnya, misalnya `[]int`, dan dapat tumbuh dengan fungsi `append`. Secara internal, slice hanyalah struktur kecil yang menyimpan tiga hal: pointer ke array dasar, panjang (length), dan kapasitas (capacity).

Cara Kerja Slice: Length, Capacity, dan Underlying Array

Memahami tiga komponen ini menjelaskan banyak perilaku slice yang terasa aneh. Length adalah jumlah elemen yang saat ini diakses, sedangkan capacity adalah seberapa banyak elemen yang bisa ditampung sebelum slice perlu mengalokasikan array baru. Saat kapasitas habis, `append` akan membuat array dasar baru yang lebih besar dan menyalin seluruh elemen lama ke sana.

Konsekuensinya, dua slice yang berasal dari array dasar yang sama bisa saling memengaruhi. Mengubah elemen di satu slice dapat mengubah elemen di slice lain selama keduanya masih menunjuk ke array dasar yang sama.

Operasi Umum pada Slice

Beberapa operasi yang paling sering dipakai:

Kombinasi Goroutine dan Slice: Hal yang Perlu Diwaspadai

Masalah muncul ketika banyak goroutine menulis ke slice yang sama tanpa pengamanan. Operasi `append` tidak aman untuk dipakai bersamaan karena beberapa goroutine bisa membaca kapasitas yang sama lalu menimpa hasil satu sama lain. Akibatnya data hilang atau program panik.

Solusinya adalah memakai `sync.Mutex` untuk mengunci akses, atau lebih idiomatis lagi, mengirim hasil lewat channel dan membiarkan satu goroutine saja yang menulis ke slice. Pola ini menjaga kepemilikan data tetap jelas dan menghindari race condition.

Praktik Terbaik Menulis Kode Go

Agar kode Go tetap bersih dan aman, biasakan beberapa hal berikut. Selalu tentukan kapasitas awal slice dengan `make([]T, 0, n)` bila jumlah elemen sudah diperkirakan, karena ini mengurangi alokasi berulang. Tutup setiap goroutine dengan mekanisme yang jelas, misalnya `context` atau `WaitGroup`, agar tidak ada goroutine yang menggantung. Terakhir, jalankan `go test -race` untuk mendeteksi race condition sejak dini.

Pola pikir ini sejalan dengan prinsip rekayasa perangkat lunak yang lebih luas, termasuk saat Anda mempelajari konsep dasar pemrograman berorientasi objek yang menekankan pentingnya enkapsulasi dan tanggung jawab yang jelas. Untuk gambaran yang lebih luas tentang ekosistem Go, artikel tentang bahasa Go untuk backend dan DevOps melengkapi pembahasan di sini.

Kesimpulan

Goroutine dan slice adalah dua fondasi yang membuat Go ringkas namun kuat. Goroutine memungkinkan konkurensi dengan biaya memori yang sangat kecil, sementara slice memberi fleksibilitas struktur data dinamis. Kuncinya adalah memahami cara kerja channel dan WaitGroup untuk sinkronisasi, serta length, capacity, dan array dasar pada slice. Dengan pemahaman itu, Anda bisa menghindari race condition dan bug halus yang sering muncul saat keduanya digabungkan.

Foto sampul oleh Mohammad Rahmani di Unsplash.

Exit mobile version