Mengenal Deadlock Dan Cara Mengatasinya Pada Golang
Pengenalan
Salah satu masalah yang terjadi ketika menggunakan concurrent atau paralel yaitu sistem deadlock. Apa itu deadlock? Deadlock adalah kejadian dimana sebuah proses concurrent atau goroutine saling menunggu (lock) sehingga tidak ada satupun goroutine yang bisa berjalan. Maka hati-hati bagi Anda jika membuat aplikasi atau program yang mengimplementasikan mutex lock and unlock dengan menggunakan goroutine. Baiklah kita akan coba saja langsung bagaimana simulasi pada program golang ketika terjadi deadlock.
Pembuatan Mutex Lock And Lock
Pertama kali kita coba buat struct untuk menampung hitungan data dengan menggungakan struct dan beberapa fungsi lock and unlock. Dan jangan lupa juga kita akan mensimulasikan ini dengan penggunaan Mutex. Berikut kode yang harus pertama kali kamu buat dibawah ini.
1type UserCount struct {
2 Name string
3 TotalCount int
4 sync.Mutex
5}
6
7func (c *UserCount) Lock() {
8 c.Mutex.Lock()
9}
10
11func (c *UserCount) Unlock() {
12 c.Mutex.Unlock()
13}
14
15func (c *UserCount) Change(amount int) {
16 c.TotalCount = c.TotalCount + amount
17}Membuat Fungsi TransferPoint
Pada fungsi TransferPoint ini kita akan mensimulasikan bahwa User satu akan melakukan transfer terhadap User yang lain sehingga nanti kita juga buat agar terjadi deadlock.
1func TransferPoint(user1 *UserCount, user2 *UserCount, amount int) {
2 user1.Lock()
3 fmt.Println("Lock processing for user : ", user1.Name)
4 user1.Change(-amount)
5
6 time.Sleep(1 * time.Second)
7
8 user2.Lock()
9 fmt.Println("Lock processing for user : ", user2.Name)
10 user2.Change(amount)
11
12 time.Sleep(1 * time.Second)
13
14 user1.Unlock()
15 user2.Unlock()
16}Selanjutnya kita akan membuat main() fungsi yang akan menjalankan simulasinya lebih lengkap seperti dibawah ini.
1func main() {
2 user1 := UserCount{
3 Name: "Ihsan",
4 TotalCount: 100,
5 }
6
7 user2 := UserCount{
8 Name: "Arif",
9 TotalCount: 100,
10 }
11
12 // call TransferPoint
13
14 fmt.Printf("User: %s count: %d\n", user1.Name, user1.TotalCount)
15 fmt.Printf("User: %s count: %d\n", user2.Name, user2.TotalCount)
16
17}1User: Ihsan count: 100
2User: Arif count: 100Percobaan Pertama Memanggil tanpa Goroutine
Pada percobaan 1, kita akan melakukan simulasi memanggil fungsi TransferPoint tanpa Goroutine seharusnya ini akan aman-aman saja dan tidak akan terjadi deadlock. Kenapa bisa begitu? karena fungsi TransferPoint ini tidak dipanggil dalam waktu yang bersamaan. Baik, Anda coba tambahkan atau panggil fungsi TransferPoint setelah inisialisasi user seperti ini.
1TransferPoint(&user1, &user2, 10)
2TransferPoint(&user2, &user1, 5)1Lock processing for user : Ihsan
2Lock processing for user : Arif
3Lock processing for user : Arif
4Lock processing for user : Ihsan
5User: Ihsan count: 95
6User: Arif count: 105- Ihsan akan melakukan transfer point 10 kepada user Arif
- Arif akan melakukan transfer point 5 kepada user Ihsan Maka, perhitungan total count-nya masing-masing itu akan seperti ini:
- Ihsan = 100 - 10 (sent) + 5 (receive) = 95
- Arif = 100 - 5 (sent) + 10 (receive) = 105
Hasil perhitungan ini adalah benar
Percobaan Dua Memanggil menggunakan Goroutine
Pada percobaan 2, kita coba simulasikan kembali yang mana ini akan terjadi deadlock saat pemanggilan fungsi TransferPoint itu berjalan menggunakan goroutine. Baik, Anda coba tambahkan paling dari fungsi tersebut dengan go dan juga tambahkan time.Sleep agar proses goroutine yang sedang berjalan akan kita tunggu selama 3 detik, maka lihat kode dibawah ini.
1go TransferPoint(&user1, &user2, 10)
2go TransferPoint(&user2, &user1, 5)
3
4time.Sleep(3 * time.Second)1Lock processing for user : Ihsan
2Lock processing for user : Arif
3User: Ihsan count: 90
4User: Arif count: 95deadlock. Baik kita coba bedah kenapa perhitungannya berbeda.- Saat User Ihsan melakukan transfer maka terjadi lock terhadap user Ihsan.
- Disaat waktu yang bersamaan User Arif juga akan melakukan transfer maka terjadi lock terhadap User Arif.
- Ketika User Ihsan akan melanjutkan proses menambahkan amount kepada Arif, ternyata user Arif dalam keadaan masih
locksehingga proses tersebut ditunggu. - Begitu juga sebaliknya User Arif juga akan melanjutkan proses menambahkan amount terhadap Ihsan tetapi ternyata user Ihsan masih
locksehingga menunggu juga. - Maka, disinilah terjadi
deadlockyang mana kedua proses tersebut saling menunggu sampai waktu yg tidak ditentukan. - Pada program ini kita ditentukan dalam waktu 3 detik, maka proses-nya pun berhenti dan perhitungannya akan menjadi salah atau kacau.
Solusi Deadlock
Setelah kita tahu bagaimana proses deadlock itu terjadi maka, bagaimana solusinya agar tidak terjadi seperti itu yaitu dengan cara melakukan Unlock setiap ada perubahan data dari user tersebut. Maka, kita akan ubah fungsi TransferPoint seperti dibawah ini.
1func TransferPoint(user1 *UserCount, user2 *UserCount, amount int) {
2 user1.Lock()
3 fmt.Println("Lock processing for user : ", user1.Name)
4 user1.Change(-amount)
5 user1.Unlock()
6
7 time.Sleep(1 * time.Second)
8
9 user2.Lock()
10 fmt.Println("Lock processing for user : ", user2.Name)
11 user2.Change(amount)
12 user2.Unlock()
13
14 time.Sleep(1 * time.Second)
15}1Lock processing for user : Arif
2Lock processing for user : Ihsan
3Lock processing for user : Arif
4Lock processing for user : Ihsan
5User: Ihsan count: 95
6User: Arif count: 105