Можно ли одновременно изменять разные элементы среза?
У меня есть срез, который содержит задачи, которые нужно выполнить, и срез, который будет содержать результаты после завершения всех задач. Вот общее описание моего процесса:
var results = make([]Result, len(jobs))
wg := sync.WaitGroup{}
for i, job := range jobs {
wg.Add(1)
go func(i int, j job) {
defer wg.Done()
var r Result = doWork(j)
results[i] = r
}(i, job)
}
wg.Wait()
// Использовать результаты
На первый взгляд, это работает, но я не тестировал это тщательно и не уверен, безопасно ли так делать. В общем, я бы не чувствовал себя комфортно, позволяя нескольким горутинам записывать в что-либо, но в данном случае каждая горутина ограничена своим собственным индексом в срезе, который заранее выделен.
Я полагаю, что альтернатива — собирать результаты через канал, но поскольку порядок результатов имеет значение, это решение кажется довольно простым. Безопасно ли записывать в элементы среза таким образом?
1 ответ(ов)
Да, это абсолютно законно: срез имеет массив в качестве своего основного хранилища данных, и, будучи составным типом, массив представляет собой последовательность "элементов", которые ведут себя как отдельные переменные с разными адресами в памяти; их одновременное изменение допустимо.
Однако не забывайте синхронизировать остановку ваших рабочих горутин с основной, прежде чем она прочитает обновленное содержимое среза.
Использовать sync.WaitGroup
для этого, как вы и делаете, вполне нормально.
Кроме того, как уже отметили @icza, вы не должны изменять саму структуру среза (которая содержит указатель на основной массив, его емкость и длину).
Существует ли цикл foreach в Go?
Почему нет ConcurrentHashSet, если есть ConcurrentHashMap?
Удалить последний элемент из массива
Golang: Как эффективно определить количество строк в файле?
Получение тега поля структуры с использованием пакета reflect в Go