unisbadri.com » Python Java Golang Typescript Kotlin Ruby Rust Dart PHP
Goroutine

Goroutine #

Go Routine di Golang #

Go routine adalah fungsi atau metode yang berjalan secara bersamaan (concurrently) dengan fungsi atau metode lainnya. Go routine sangat ringan dibandingkan dengan thread OS karena banyak go routine dapat dijalankan dalam satu thread OS. Go routine memungkinkan pemrograman concurrent dan parallel yang efisien di Golang.

Cara Membuat Go Routine #

Untuk membuat go routine, cukup tambahkan kata kunci go sebelum pemanggilan fungsi.

package main

import (
    "fmt"
    "time"
)

func sayHello() {
    fmt.Println("Hello, world!")
}

func main() {
    go sayHello() // Memanggil fungsi sebagai go routine
    time.Sleep(1 * time.Second) // Menunggu 1 detik untuk melihat output
    fmt.Println("Main function")
}

Dalam contoh di atas, sayHello() dipanggil sebagai go routine. Fungsi utama (main) akan melanjutkan eksekusi tanpa menunggu sayHello selesai, sehingga Main function mungkin dicetak sebelum atau setelah Hello, world!.

Handling Shared Data #

Ketika beberapa go routine mengakses dan memodifikasi data yang sama, ada risiko kondisi balapan (race condition). Golang menyediakan beberapa cara untuk menangani data bersama (shared data) dengan aman:

  1. Channels
  2. Mutexes

Channels #

Channels adalah cara aman untuk berkomunikasi antara go routine. Channels dapat digunakan untuk mengirim data dari satu go routine ke yang lain.

package main

import (
    "fmt"
)

func main() {
    messages := make(chan string)

    go func() {
        messages <- "ping" // Mengirim data ke channel
    }()

    msg := <-messages // Menerima data dari channel
    fmt.Println(msg)
}

Dalam contoh di atas, data dikirim ke channel messages dalam go routine dan diterima di fungsi utama.

Mutexes #

Mutexes (mutual exclusion) digunakan untuk mengunci dan membuka kunci akses ke data bersama untuk memastikan hanya satu go routine yang dapat mengakses data pada satu waktu.

package main

import (
    "fmt"
    "sync"
)

var (
    counter int
    mutex   sync.Mutex
)

func increment(wg *sync.WaitGroup) {
    defer wg.Done()
    mutex.Lock()   // Mengunci akses ke counter
    counter++
    mutex.Unlock() // Membuka kunci akses ke counter
}

func main() {
    var wg sync.WaitGroup

    for i := 0; i < 10; i++ {
        wg.Add(1)
        go increment(&wg)
    }

    wg.Wait()
    fmt.Println("Counter:", counter)
}

Dalam contoh ini, increment adalah go routine yang mengakses dan memodifikasi variabel counter. mutex digunakan untuk mengunci akses ke counter sehingga hanya satu go routine yang dapat memodifikasi counter pada satu waktu.

Contoh Lengkap #

Berikut adalah contoh lengkap yang menunjukkan penggunaan go routine, channels, dan mutexes untuk menangani data bersama:

package main

import (
    "fmt"
    "sync"
    "time"
)

// Menggunakan channel untuk komunikasi antar go routine
func producer(ch chan<- int, wg *sync.WaitGroup) {
    defer wg.Done()
    for i := 0; i < 5; i++ {
        fmt.Println("Producing:", i)
        ch <- i
        time.Sleep(time.Millisecond * 500)
    }
    close(ch)
}

// Menggunakan mutex untuk mengelola data bersama
func consumer(ch <-chan int, wg *sync.WaitGroup, mutex *sync.Mutex, counter *int) {
    defer wg.Done()
    for item := range ch {
        fmt.Println("Consuming:", item)
        mutex.Lock()
        *counter++
        mutex.Unlock()
        time.Sleep(time.Millisecond * 500)
    }
}

func main() {
    ch := make(chan int)
    var wg sync.WaitGroup
    var mutex sync.Mutex
    counter := 0

    wg.Add(1)
    go producer(ch, &wg)

    wg.Add(1)
    go consumer(ch, &wg, &mutex, &counter)

    wg.Wait()
    fmt.Println("Final Counter:", counter)
}

Dalam contoh di atas:

  • Fungsi producer menghasilkan angka dan mengirimkannya melalui channel.
  • Fungsi consumer menerima angka dari channel dan menginkrementasi counter.
  • Mutex digunakan untuk memastikan hanya satu go routine yang dapat mengakses dan memodifikasi counter pada satu waktu.

Menangani Race Condition di Golang #

Race condition terjadi ketika dua atau lebih go routine mengakses dan memodifikasi data yang sama secara bersamaan tanpa koordinasi yang tepat. Ini dapat menyebabkan perilaku tak terduga dan bug yang sulit dilacak. Untuk menangani race condition di Golang, ada beberapa pendekatan utama yang dapat digunakan:

  1. Menggunakan Channels
  2. Menggunakan Mutexes (Mutual Exclusion)
  3. Menggunakan Atomic Operations

Menggunakan Channels #

Channels di Golang adalah cara yang aman untuk berkomunikasi antara go routine. Channels dapat digunakan untuk menghindari race condition dengan mengirimkan data antar go routine, memastikan hanya satu go routine yang mengakses data pada satu waktu.

Contoh menggunakan channels:

package main

import (
    "fmt"
    "time"
)

func incrementer(ch chan int) {
    for i := 0; i < 5; i++ {
        ch <- i
        time.Sleep(100 * time.Millisecond)
    }
    close(ch)
}

func main() {
    ch := make(chan int)

    go incrementer(ch)

    for val := range ch {
        fmt.Println("Received:", val)
    }
}

Dalam contoh ini, data dikirim dari go routine incrementer ke fungsi utama melalui channel ch, sehingga tidak ada race condition.

Menggunakan Mutexes #

Mutex (mutual exclusion) adalah mekanisme untuk memastikan bahwa hanya satu go routine yang dapat mengakses atau memodifikasi data tertentu pada satu waktu.

Contoh menggunakan mutexes:

package main

import (
    "fmt"
    "sync"
)

var (
    counter int
    mutex   sync.Mutex
)

func increment(wg *sync.WaitGroup) {
    defer wg.Done()
    for i := 0; i < 1000; i++ {
        mutex.Lock()
        counter++
        mutex.Unlock()
    }
}

func main() {
    var wg sync.WaitGroup

    for i := 0; i < 5; i++ {
        wg.Add(1)
        go increment(&wg)
    }

    wg.Wait()
    fmt.Println("Final Counter:", counter)
}

Dalam contoh ini, mutex.Lock() dan mutex.Unlock() memastikan bahwa hanya satu go routine yang dapat mengakses dan memodifikasi counter pada satu waktu, menghindari race condition.

Menggunakan Atomic Operations #

Golang menyediakan paket sync/atomic yang mendukung operasi atomik pada variabel. Operasi atomik adalah operasi yang tidak dapat dipisahkan dan dilakukan sepenuhnya atau tidak sama sekali, sehingga memastikan keamanan data dalam lingkungan concurrent.

Contoh menggunakan operasi atomik:

package main

import (
    "fmt"
    "sync"
    "sync/atomic"
)

var counter int64

func increment(wg *sync.WaitGroup) {
    defer wg.Done()
    for i := 0; i < 1000; i++ {
        atomic.AddInt64(&counter, 1)
    }
}

func main() {
    var wg sync.WaitGroup

    for i := 0; i < 5; i++ {
        wg.Add(1)
        go increment(&wg)
    }

    wg.Wait()
    fmt.Println("Final Counter:", counter)
}

Dalam contoh ini, atomic.AddInt64 digunakan untuk menambah nilai counter dengan aman dalam lingkungan concurrent.

Deteksi Race Condition #

Golang menyediakan alat -race untuk mendeteksi race condition. Anda dapat menjalankan program Anda dengan flag -race untuk mendeteksi dan melaporkan race condition.

Contoh menjalankan program dengan deteksi race condition:

go run -race main.go

Jika ada race condition, alat ini akan melaporkannya, membantu Anda mengidentifikasi dan memperbaiki masalah tersebut.

Untuk menangani race condition di Golang, Anda dapat menggunakan channels untuk komunikasi aman antar go routine, mutexes untuk memastikan eksklusivitas akses ke data bersama, atau operasi atomik untuk melakukan operasi yang tidak dapat dipisahkan. Deteksi race condition dapat dilakukan dengan menggunakan alat -race yang disediakan oleh Golang. Pendekatan ini membantu memastikan bahwa program concurrent Anda berjalan dengan aman dan bebas dari race condition.

Kesimpulan #

Go routine memungkinkan program Golang untuk menjalankan fungsi secara bersamaan dengan efisien. Namun, saat menangani data bersama, penting untuk menghindari kondisi balapan menggunakan tools seperti channels dan mutexes. Channels menyediakan cara yang aman untuk komunikasi antara go routine, sementara mutexes memastikan eksklusivitas akses ke data bersama.

« Vendoring
I/O »