Прежде всего обратите внимание, что эта попытка выполнить вычисления одновременно предполагает, что методы SetVec()
и AtVec()
безопасны для одновременного использования с различными индексами. Если это не так, то попытка решения по своей сути небезопасна и может привести к скачкам данных и неопределенному поведению.
wg.Done()
должен быть вызван, чтобы сигнализировать, что рабочая программа завершила свою работу. Но только , когда рутин закончил свою работу.
В вашем случае это не только функция sigmoid()
, которая выполняется в рабочей программе, а zs.SetVec()
. Поэтому вы должны позвонить wg.Done()
, когда zs.SetVec()
вернется, но не раньше.
Одним из способов было бы добавить wg.Done()
в конец метода SetVec()
(это также может быть defer wg.Done()
в его начале), но было бы невозможно ввести эту зависимость (SetVec()
не должен знать о каких-либо группах ожидания и процедурах, это серьезно ограничило бы его удобство использования.
Самый простой и понятный способ в этом случае - запустить анонимную функцию (литерал функции) в качестве рабочей программы, в которой вы можете вызвать zs.SetVec()
, и в которой вы можете вызвать wg.Defer()
, как только упомянуто выше. функция вернулась.
Примерно так:
for i := 0; i < zs.Len(); i++ {
wg.Add(1)
go func() {
zs.SetVec(i, sigmoid(zs.AtVec(i)))
wg.Done()
}()
}
wg.Wait()
Но только это не будет работать, так как литерал функции (закрытие) ссылается на переменную цикла, которая изменяется одновременно, поэтому литерал функции должен работать со своей собственной копией, например ::
for i := 0; i < zs.Len(); i++ {
wg.Add(1)
go func(i int) {
zs.SetVec(i, sigmoid(zs.AtVec(i)))
wg.Done()
}(i)
}
wg.Wait()
Также обратите внимание, что у подпрограмм (хотя они могут быть легкими) есть накладные расходы. Если работа, которую они выполняют, «мала», накладные расходы могут перевесить выигрыш в производительности при использовании нескольких ядер / потоков, и в целом вы не сможете повысить производительность, выполняя такие небольшие задачи одновременно (черт, вы можете даже сделать хуже, чем без использования программ) , Измерить.
Также вы используете мини-программы для выполнения минимальной работы, вы можете улучшить производительность, не «выбрасывая» программы после того, как они выполнят свою «крошечную» работу, но вы можете их «использовать» повторно. См. Связанный вопрос: Это пул рабочих потоков идиоматического в Go?