CodeNebula
Go⚡Concurrencyintermediate

The Race Detector

Go · Concurrency · Run with go run

Explanation

A data race occurs when two goroutines access the same memory concurrently and at least one writes, with no synchronization. Go ships a built-in race detector enabled with the `-race` flag (`go run -race`, `go test -race`, `go build -race`). It instruments memory accesses and reports the conflicting goroutines, stacks, and addresses at runtime.

Real-World Scenario

A test passes locally but fails intermittently in CI. Running the test suite with `go test -race ./...` reveals that two goroutines mutate a shared map without a mutex — the detector pinpoints the exact lines, turning a heisenbug into a one-line fix (`sync.Mutex` or a channel).

Quick Snippet

snippet.go
// This code has a data race — run with: go run -race race_detector.go
var counter int

func main() {
    var wg sync.WaitGroup
    for i := 0; i < 100; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            counter++ // RACE: unsynchronized write
        }()
    }
    wg.Wait()
}

// The fix: protect counter with sync.Mutex or use atomic.Int64.
// Always run tests with -race in CI to catch races early.

Full Example

race_detector.go
// Compile: go run race_detector.go
// Detect races: go run -race race_detector.go
// Output:
//   final count: 100
//
// The racy version is shown in raceyIncrement (commented call). The
// synchronized version below uses atomic.Int64 and is race-free — the
// race detector (-race) reports nothing for it.
package main

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

// raceyIncrement has a data race: 100 goroutines write `counter`
// with no synchronization. `go run -race` flags it.
func raceyIncrement() {
	var counter int
	var wg sync.WaitGroup
	for i := 0; i < 100; i++ {
		wg.Add(1)
		go func() { defer wg.Done(); counter++ }() // RACE
	}
	wg.Wait()
	_ = counter
}

// safeIncrement is the fix: atomic operations are race-free.
func safeIncrement() int64 {
	var counter atomic.Int64
	var wg sync.WaitGroup
	for i := 0; i < 100; i++ {
		wg.Add(1)
		go func() { defer wg.Done(); counter.Add(1) }()
	}
	wg.Wait()
	return counter.Load()
}

func main() {
	// raceyIncrement() // uncomment and run with -race to see the report
	fmt.Println("final count:", safeIncrement())
}
Run command
go run race_detector.go