- Published on
Race Condition
- Authors
- Name
- Moch Lutfi
- @kaptenupi
A data race in Go (or any other programming language) occurs when two or more goroutines access the same shared data concurrently and at least one of those accesses is a write operation. This can lead to unpredictable behavior and can cause bugs in your code.
In Go, data races can occur when multiple goroutines access the same variables or memory locations without proper synchronization. For example, consider the following code:
In this code, two goroutines are incrementing the same counter
variable concurrently. This can lead to a data race because both goroutines are writing to the same memory location without any synchronization mechanism. How to find out it race condition occurred ? Try run with flag --race
like example below
_21❯ go run --race main.go_21==================_21WARNING: DATA RACE_21Read at 0x000000548700 by goroutine 7:_21 main.incrementCounter()_21 /home/upix/code/oss/golang-playground/main.go:6 +0x29_21_21Previous write at 0x000000548700 by goroutine 6:_21 main.incrementCounter()_21 /home/upix/code/oss/golang-playground/main.go:6 +0x44_21_21Goroutine 7 (running) created at:_21 main.main()_21 /home/upix/code/oss/golang-playground/main.go:11 +0x35_21_21Goroutine 6 (finished) created at:_21 main.main()_21 /home/upix/code/oss/golang-playground/main.go:10 +0x29_21==================_21Found 1 data race(s)_21exit status 66
To avoid data races in Go, you need to use synchronization primitives such as channels, mutexes, or atomic operations. For example, you can use a mutex to ensure that only one goroutine at a time can access the shared data:
_15package main_15_15var counter int_15var mu sync.Mutex_15_15func incrementCounter() {_15 mu.Lock()_15 defer mu.Unlock()_15 counter++_15}_15_15func main() {_15 go incrementCounter()_15 go incrementCounter()_15}
With this implementation, the mutex ensures that only one goroutine can access the counter
variable at a time, avoiding any data races.
The other alternative is use the atomic
package in Go to perform atomic operations on shared variables, ensuring that they are executed as a single indivisible operation. For example:
_14package main_14_14import "sync/atomic"_14_14var counter int32_14_14func incrementCounter() {_14 atomic.AddInt32(&counter, 1)_14}_14_14func main() {_14 go incrementCounter()_14 go incrementCounter()_14}