DEV Community

ynwd
ynwd

Posted on

Data Races di Golang: Fixing with Mutex

Sebelumnya, kita telah membahas data race di golang dan channel. Sekarang kita akan mencoba memperbaiki data races dengan mutex.

Tapi sebelumnya, kita akan menambahkan sebuah struct text serta getter-setter-nya dan mengubah fungsi getText(), menjadi seperti ini:

package main

import (
    "fmt"
)

func getText() string {
    i := text{txt: "hi"}
    go func() {
        i.Set("hello")
    }()
    return i.Get()
}

type text struct {
    txt string
}

func (t *text) Set(txt string) {
    t.txt = txt
}

func (t *text) Get() string {
    return t.txt
}

func main() {
    fmt.Println(getText())
}

Enter fullscreen mode Exit fullscreen mode

Kalau dilihat secara sequence diagram, ia mirip dengan kode sebelumnya.

  • Go routine #1 : main()
  • Go routine #2 : fungsi di mana i.Set() dipanggil.

data races

Dan jika kita jalankan dengan -race:

go run -race main.go  
Enter fullscreen mode Exit fullscreen mode

Maka ia juga akan menghasilkan warning:

hi
==================
WARNING: DATA RACE
Write at 0x00c000012230 by goroutine 7:
  main.(*text).Set()
      /Users/pro/Documents/apps/cms/cmd/main.go:20 +0x33
  main.getText.func1()
      /Users/pro/Documents/apps/cms/cmd/main.go:10 +0x2e

Previous read at 0x00c000012230 by main goroutine:
  main.(*text).Get()
      /Users/pro/Documents/apps/cms/cmd/main.go:24 +0xda
  main.getText()
      /Users/pro/Documents/apps/cms/cmd/main.go:12 +0xf1
  main.main()
      /Users/pro/Documents/apps/cms/cmd/main.go:28 +0x24

Goroutine 7 (running) created at:
  main.getText()
      /Users/pro/Documents/apps/cms/cmd/main.go:9 +0xd0
  main.main()
      /Users/pro/Documents/apps/cms/cmd/main.go:28 +0x24
==================
Found 1 data race(s)
exit status 66
Enter fullscreen mode Exit fullscreen mode

Solusi dengan mutex

Mutex gunanya untuk mencegah agar sebuah variabel tidak diakses secara bersamaan.

Implementasinya sangat mudah. Kita tinggal menempatkan saja method Lock dan Unlock di variabel yang terkena data races.

package main

import (
    "fmt"
    "sync"
)

func getText() string {
    i := text{txt: "hi"}
    go func() {
        i.Set("hello")
    }()
    return i.Get()
}

type text struct {
    txt   string
    mutex sync.Mutex
}

func (t *text) Set(txt string) {
    t.mutex.Lock()
    defer t.mutex.Unlock()
    t.txt = txt
}

func (t *text) Get() string {
    t.mutex.Lock()
    defer t.mutex.Unlock()
    return t.txt
}

func main() {
    fmt.Println(getText())
}

Enter fullscreen mode Exit fullscreen mode

Jika kita jalankan dengan -race, hasilnya:

$ go run -race main.go                                                     main*
hi
Enter fullscreen mode Exit fullscreen mode

mutex prevent datarace

Yang menjadi pertanyaan: mengapa hasilnya tetap hi?

Jawabannya adalah: karena go routine #1 (main), selesainya lebih dulu dan langsung exit.

Walaupun go routine ke-2 sedang berjalan, karena go routine #1 sudah exit duluan, ya sudah, program selesai.

Diagram data races

Discussion (0)