0

Можно ли одновременно изменять разные элементы среза?

13

У меня есть срез, который содержит задачи, которые нужно выполнить, и срез, который будет содержать результаты после завершения всех задач. Вот общее описание моего процесса:

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 ответ(ов)

0

Да, это абсолютно законно: срез имеет массив в качестве своего основного хранилища данных, и, будучи составным типом, массив представляет собой последовательность "элементов", которые ведут себя как отдельные переменные с разными адресами в памяти; их одновременное изменение допустимо.

Однако не забывайте синхронизировать остановку ваших рабочих горутин с основной, прежде чем она прочитает обновленное содержимое среза.

Использовать sync.WaitGroup для этого, как вы и делаете, вполне нормально.

Кроме того, как уже отметили @icza, вы не должны изменять саму структуру среза (которая содержит указатель на основной массив, его емкость и длину).

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь