← Back to Go Course | Chapter 11: Concurrency | Lesson 7 of 8

Race Conditions

A race condition happens when two goroutines rush to touch the same piece of data at the same time, and whoever wins changes the outcome unpredictably.

What a Race Condition Looks Like

When two or more goroutines read and write the same variable without any coordination, the final result depends on unpredictable timing -- the same program can produce different, sometimes wrong, results across separate runs.

Example: What a Race Condition Looks Like

markup
package main

import (
	"fmt"
	"sync"
)

func main() {
	total := 0
	var wg sync.WaitGroup
	for i := 0; i < 1000; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			total++ // unsynchronized: a real race in production code
		}()
	}
	wg.Wait()
	fmt.Println("total (may vary if truly racy):", total)
}

Detecting Races with -race

Go ships a race detector, enabled with 'go run -race' or 'go test -race', which instruments memory accesses and reports the exact goroutines and lines involved when it observes a genuine race, even if the output happened to look correct that run.

Note: Always run tests with -race in CI when the code involves goroutines sharing data.

Example: Detecting Races with -race

bash
go run -race main.go

⚠️ Run this command in your terminal.

Fixing a Race with a Mutex

The standard fix for a race on shared data is to protect every access to it with a mutex, ensuring only one goroutine can read or write the variable at any given instant.

Example: Fixing a Race with a Mutex

markup
package main

import (
	"fmt"
	"sync"
)

func main() {
	var mu sync.Mutex
	total := 0
	var wg sync.WaitGroup
	for i := 0; i < 1000; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			mu.Lock()
			total++
			mu.Unlock()
		}()
	}
	wg.Wait()
	fmt.Println("total:", total)
}
Common Mistakes
  1. Assuming a program that 'usually works' during testing has no race condition -- races are timing-dependent and can hide until production load exposes them.
  2. Reading a shared variable from one goroutine while another writes it, without a mutex or channel, believing simple reads are always safe.
  3. Never running 'go test -race' or 'go run -race', missing races that only the race detector reliably surfaces.
Chapter Summary
  • A race condition occurs when multiple goroutines access shared data concurrently and at least one writes it, without synchronization.
  • Races produce unpredictable results that can vary between runs, making them hard to debug by inspection alone.
  • Go's built-in race detector (-race flag) instruments a program to catch races at runtime.
  • Fixing a race means synchronizing access with a mutex, channel, or avoiding the shared mutable state entirely.
🔒

Chapter Quiz — Complete all 8 topics to unlock

0/8 topics done

Complete these topics first:

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.