Slices, maps and strings · 3 MIN READ

Shared slices and concurrent append

Separate shared headers, shared elements and safe collection of concurrent results.

A goroutine does not make its arguments private. If two workers share a slice, you must reason about the slice descriptor and the array it refers to. Making extra goroutines can expose an ownership mistake that a sequential example concealed.

01

Why append needs coordination

Concurrent append to one shared slice variable reads and writes the same descriptor and may write the same backing storage. Even if capacity is large enough, append still changes the shared length. Reserving capacity is a performance choice, not synchronization.

go
// Unsafe: both goroutines update the same slice.
var results []int
go func() { results = append(results, 1) }()
go func() { results = append(results, 2) }()
Read the code

The outcome is not a reliable permutation of [1 2]. This is a deliberate counterexample, not code to copy. A passing test without the race detector does not establish safety.

02

Give each worker a separate slot

A pre-sized slice permits independent element writes when each worker owns a different index, no worker changes the descriptor, and the reader waits for every writer. Elements that themselves contain shared pointers need their own ownership rules.

go
results := make([]int, len(inputs))
var wg sync.WaitGroup
for i, value := range inputs {
    wg.Add(1)
    go func(i, value int) {
        defer wg.Done()
        results[i] = value * 2
    }(i, value)
}
wg.Wait() // Read results only after the writers finish.
Read the code

Index i is assigned to exactly one goroutine. WaitGroup orders the final read after the completed writes. For very large inputs, combine this ownership pattern with a bounded worker pool rather than starting a goroutine per item.

03

Use a single collector for variable-length output

If each job returns an unknown number of values, let workers send results to one owner that appends, or protect every access with the same mutex. A channel transfers values; it does not deep-copy a slice payload. The sender must stop mutating transferred storage or clone it first. Run race-enabled tests on a supported desktop toolchain; the app does not offer a native race detector.

TAKE THIS WITH YOU

Concurrency safety comes from ownership and synchronization, not spare capacity. Distinct indexes plus a final join differ from concurrent append.

Check your understanding

Which statement is correct about concurrent append?

Go to the source

Progress stays in this browser. No account needed.