Ответ 1
Правило простое: если несколько goroutines получают доступ к переменной одновременно, и по крайней мере один из этих запросов является записью, тогда требуется синхронизация.
Ваш пример не нарушает это правило. Вы не записываете значение среза (заголовок среза), вы его читаете (неявно, когда вы его индексируете).
Вы не читаете элементы среза, вы только изменяете элементы среза. И каждый goroutine только модифицирует один, другой, назначенный элемент среза. И поскольку каждый элемент среза имеет свой собственный адрес (собственное пространство памяти), они похожи на разные переменные. Это описано в Spec: Variables:
Структурированные переменные типов массива, среза и структуры имеют элементы и поля, которые могут быть рассмотрены индивидуально. Каждый такой элемент действует как переменная.
Следует иметь в виду, что вы не можете читать результаты из среза results
без синхронизации. И waitgroup, которую вы использовали в вашем примере, является достаточной синхронизацией. Вы можете прочитать срез, как только wg.Wait()
вернется, потому что это может произойти только после того, как все рабочие wg.Done()
называются wg.Done()
, и ни один из рабочих wg.Done()
элементы после того, как они вызвали wg.Done()
.
Например, это действительный (безопасный) способ проверки/обработки результатов:
wg.Wait()
// Safe to read results after the above synchronization point:
fmt.Println(results)
Но если вы попытаетесь получить доступ к элементам results
перед wg.Wait()
, то гонка данных:
// This is data race! Goroutines might still run and modify elements of results!
fmt.Println(results)
wg.Wait()