Tips memahami Goroutines pada Go
Bekerja dengan Goroutines adalah sebuah momok yang membingungkan saya di awal development dengan Go. Goroutines tidak hanya menjadi andalan Go dalam melakukan proses secara simultan, namun juga sebuah skill yang wajib dikuasai dalam perancangan sebuah program menggunakan Go.
Channel
Pada dasarnya goroutines hanyalah sebutan dari sebuah fungsi yang terdapat penggalan kata go di depannya
Sepenggal baris kode diatas merupakan contoh dari goroutines yang melakukan proses cetak ke *stdout* sistem. Kode program tersebut akan berjalan, beriringan dengan proses utama hingga prosesnya selesai. Singkat kata, fungsi tersebut tidak lagi tergantung pada block operation yang terjadi pada proses utamanya.
Kesulitan baru akan muncul ketika main proses atau proses lainnya ingin berinterakasi dengan goroutines tersebut. Karena groroutines memiliki jalurnya sendiri dalam proses simultan, diperlukan sebuah mekanisme yang dapat menjembatani proses goroutines dengan proses lainnya. Mekanisme tersebut bernama Channel.
Penggalan kode diatas merupakan contoh dari sebuah channel dengan tipe Error di Go.
Channel merupakan sebuah interface dari sebuah tipe data atau objek dimana seolah-olah menjadi tunnel atau jembatan yang terhubung antara pengirim dan penerimanya.
Kutipan kode diatas merupakan ilustrasi channel dengan tipe integer melakukan komunikasi dengan mengirimkan hasil dari penjumlahan ke dalam blok proses utama.
Jika program diatas dijalankan, maka akan proses utama akan menunggu proses dari fungsi goroutines untuk selesai (ilustrasinya membutuhkan waktu 5 detik), setelahnya proses utama akan mencetak hasil ke console atau terminal. Dari sini terlihat bahwa proses utama atau proses yang menerima hasil akan terkena blocking (pause) sampai hasil dari goroutines dikirimkan melalui channel result.
Alur Data pada Channel
Alur data flow pada channel ditandai dengan penulisan anak panah ke variabelnya. Mengutip contoh kode sebelumnya, penulisan res<- merupakan statemen untuk mengirimkan data (send) hasil penjumlahan untuk masuk melalui channel, lalu pada main kode program terdapat <-result (receive) yang merupakan proses pengambilan data penjumlahan.
Buffered Channel
Buffered Channel dapat di analogikan seperti sebuah tunnel yang memiliki kuota mengenai berapa banyak jumlah objek yang dapat muat masuk sekaligus didalamnya.
Secara default sebuah channel tidak memiliki buffer atau bisa disebut juga dengan unbuffered channel, alias hanya satu objek saja yang dapat dikirimkan melalui channel tersebut, sampai objek itu keluar (atau digunakan) dari channel, barulah objek berikutnya dapat masuk melewati channel yang sama.
Lalu bagaimana jika ingin mengirimkan beberapa objek di satu waktu secara bersamaan? jawabannya dengan menggunakan Buffered Channel.
Dengan penggunaan Buffered Channel kita dapat melakukan proses pengiriman data sekaligus sebanyak jumlah buffer (capacity) yang disiapkan sebelumnya.
Dalam contoh program diatas, channel results merupakan Unbuffered Channel, yang menjadikan fungsi add() hanya dapat mengirimkan hasil penjumlahan begitu channel results tersedia atau telah melakukan proses receive.
Fungsi printer() sebagai receiver memiliki waktu proses selama 1 detik, karena melakukan proses menggunakan unbuffered channel, maka antar goroutines add() harus saling menunggu untuk dapat mengirimkan hasil berikutnya.
Terlihat dari hasil eksekusi kode program diatas, fungsi add() hanya akan dapat mengirimkan data ke channel ketika proses receive telah dilakukan, karena itu proses cetak ke terminal selalu berdampingan antara pengiriman dan pemrosesan data hasil.
Mengambil dari contoh kode sebelumnya, dengan merubah sebaris kode seperti dibawah ini, akan menjadikan proses unbuffered channel menjadi buffered channel dimana channel akan memiliki kuota sebanyak 3x int.
Berikut kode yang sudah dimodifikasi agar mengadopsi Buffered channel
Dengan menggunakan buffered channel, proses pengiriman hasil dari fungsi add() dapat langsung dilakukan tanpa harus menunggu channel results tersedia. Hal ini terlihat dari hasil output di bawah ini.
Closing Channel
Channel yang telah selesai digunakan harus ditutup dengan keyword close, hal ini menandakan proses transmisi data melalui channel tersebut tidak lagi dapat dilakukan. Selanjutnya channel ini dibersihkan melalui garbage collection.
GOMAXPROCS
Dalam prakteknya setelah versi go 1.5, default value dari GOMAXPROCS akan diset dengan sejumlah CPU yang tersedia pada komputer yang menjalankannya. Hal ini tentunya merupakan hal yang positif mengingat dalam versi sebelumnya, developer harus mendeteksi berapa jumlah CPU core yang ada dan melakukan set GOMAXPROCS secara manual.
Mengapa hal ini sangat berpengaruh? Gorotines pada dasarnya tidak selalu membutuhkan multi CPU untuk bekerja, beberapa developer mungkin berfikir dengan menambah jumlah CPU, jalannya sebuah program akan lebih maksimal, sayangnya tidak selalu seperti itu.
Goroutines mirip seperti Thread pada Java, pada dasarnya menggunakan Memori (RAM) untuk dapat beroperasi.
Goroutines tidak harus selalu berjalan pada CPU yang berbeda untuk memaksimalkan multitasking-nya, karena dalam prosesnya Go secara otomatis akan mengatur berapa dan bagaimana goroutines akan bekerja di satu core ataupun multi-core, karena setelah Go versi 1.5, developer tidak perlu lagi memikirkan bagaimana kolaborasi gorotines dalam memanfaatkan multi-core CPU agar proses dapat bekerja lebih cepat dan efisien.
Berkomunikasilah dengan Data bukan Pointer
Don't communicate by sharing memory; share memory by communicating. (R. Pike)
Kutipan diatas menjelaskan kepada kita tentang bagaimana sebaiknya sebuah proses concurrency berinteraksi antara satu dengan yang lainnya. Meskipun pada Go kita dapat mengirimkan pointer (alamat memori) namun hal itu sebaiknya dihindari ketika menggunakan goroutines.
Melakukan komunikasi antar goroutines dengan cara sharing memory adalah hal yang patut dihindari, karena dapat berpotensi abuse pada alamat memory yang di share ke beberapa proses, dimana memungkinkan munculnya error bila terdapat ketidak konsistenan program dalam memanipulasi memory address. Oleh karena itu dalam goroutines sebaiknya hanya mengirimkan objek statis atau nilai primitif saja.
Semoga tulisan saya ini cukup membantu kalian dalam memahami cara kerja dan aturan-aturan yang ada pada Goroutines.
Kira-kira apa yang menurutmu paling sulit dalam penggunaan goroutines atau channel ini? silahkan tinggalkan komentar dibawah.