Skip to content

Enhance Your GoLang Code Quality: Understand and Address Race Condition

Race condition is a common problem in Golang. A race condition is a situation where there are several requests simultaneously and depending on the time sequence of events, which can cause damage to the system process. In this article, we will cover how to handle race conditions in Golang using mutex and channel.

Normal Condition

package main

import (
	"fmt"
	"time"
)

type Counter struct {
	value int
}

func (c *Counter) Increment() {
	defer time.Sleep(time.Second) // simulate some work
	c.value++
}

func main() {
	var (
		counter = Counter{
			value: 0,
		}
	)

	for i := 0; i < 1000; i++ {
		counter.Increment()
	}

	fmt.Printf("Counter value is %d\n", counter.value)
}

Output:

Example Normal Condition note: code is include time sleep to simulate some work, then is running time is -+ 17 minutes (1 second * 1000 counting).

Race Condition

package main

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

type Counter struct {
	value int
	wg    *sync.WaitGroup
}

func (c *Counter) Increment() {
	defer time.Sleep(time.Second) // simulate some work
	defer c.wg.Done()

	c.value++
}

func main() {
	var (
		waitGroup sync.WaitGroup
		counter   = Counter{
			wg:    &waitGroup,
			value: 0,
		}
	)

	for i := 0; i < 1000; i++ {
		waitGroup.Add(1)
		go counter.Increment()
	}

	waitGroup.Wait()
	fmt.Printf("Counter value is %d\n", counter.value)
}

Output:

Example Race Condition

Resolve Race Condition With Mutex

package main

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

type Counter struct {
	value int
	wg    *sync.WaitGroup
	mu    *sync.Mutex
}

func (c *Counter) Increment() {
	defer time.Sleep(time.Second) // simulate some work
	defer c.wg.Done()

	c.mu.Lock()
	defer c.mu.Unlock()

	c.value++
}

func main() {
	var (
		waitGroup sync.WaitGroup
		m         sync.Mutex
		counter   = Counter{
			wg:    &waitGroup,
			mu:    &m,
			value: 0,
		}
	)

	for i := 0; i < 1000; i++ {
		waitGroup.Add(1)
		go counter.Increment()
	}

	waitGroup.Wait()
	fmt.Printf("Counter value is %d\n", counter.value)
}

Output:

Example Mutex

Resolve Race Condition With Channel

package main

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

type Counter struct {
	value int
	wg    *sync.WaitGroup
	ch    chan bool
}

func (c *Counter) Increment() {
	defer time.Sleep(time.Second) // simulate some work
	defer c.wg.Done()

	c.ch <- true
	c.value++
	<-c.ch

}

func main() {
	var (
		waitGroup sync.WaitGroup
		ch        = make(chan bool, 1)
		counter   = Counter{
			wg:    &waitGroup,
			ch:    ch,
			value: 0,
		}
	)

	for i := 0; i < 1000; i++ {
		waitGroup.Add(1)
		go counter.Increment()
	}

	waitGroup.Wait()
	fmt.Printf("Counter value is %d\n", counter.value)
}

Output:

Example Channel

Conclusion

Race condition is a common problem in Golang. In this article, we will cover how to handle race condition in Golang using mutex and channel.

Reference