Sobes.tech
Middle

What rules should be considered when declaring a variable in a goroutine?

sobes.tech AI

Answer from AI

// Rule 1: Avoid sharing variables without synchronization.
// Goroutines run in parallel, and access to the same variable
// without locks or atomic operations can lead to race conditions.
// Use sync.Mutex or channels for safe access.

// Example with race condition:
var counter int
func incrementBad() {
    counter++ // Might be unsafe for concurrent access
}

// Example with Mutex:
import "sync"
var (
    counterSafe int
    mutex sync.Mutex
)
func incrementSafe() {
    mutex.Lock()
    defer mutex.Unlock()
    counterSafe++
}
// Rule 2: Prefer passing data between goroutines via channels.
// Go idiom: "Do not share memory by communicating, but communicate by sharing memory".
// Channels provide synchronization and order access to data.

// Example with channel:
func worker(id int, jobs <-chan int, results chan<- int) {
    for j := range jobs {
        // Process task
        results <- j * 2 // Send result
    }
}
// Rule 3: Be cautious with closures capturing variables from outer scope.
// A goroutine may start executing after the loop or function in which it was created has finished.
// If the closure captures a variable by reference (default behavior),
// it may get an unexpected value. Copy the variable if needed inside the goroutine.

// Example with unexpected closure behavior:
func badGoroutineLoop() {
    for i := 0; i < 5; i++ {
        go func() {
            // By the time all goroutines start, i is most likely 5
            // fmt.Println(i) // Output: 5 5 5 5 5
        }()
    }
}

// Example with correct variable capture:
func goodGoroutineLoop() {
    for i := 0; i < 5; i++ {
        iLocal := i // Create a local copy of the variable
        go func() {
            // fmt.Println(iLocal) // Output: 0 1 2 3 4 (order may vary)
        }()
    }
}
// Rule 4: Use sync.WaitGroup to wait for all goroutines to finish.
// This prevents premature termination of the main goroutine, e.g., main(),
// before parallel tasks are completed.

// Example with WaitGroup:
import "sync"
var wg sync.WaitGroup
func main() {
    for i := 0; i < 3; i++ {
        wg.Add(1) // Increment wait counter
        go func(id int) {
            defer wg.Done() // Signal completion
            // Work...
        }(i)
    }
    wg.Wait() // Wait until counter is zero
    // All goroutines are finished
}