На собесе встречаются два близких варианта. Нужно сделать оба.
1. Разбить слайс между горутинами.
// Sum возвращает сумму nums, разбив работу на workers горутин (workers < 1 считается как 1).
func Sum(nums []int, workers int) int
- Слайс делится на
workers непересекающихся кусков примерно равной длины,
каждый кусок суммирует своя горутина, результаты складываются.
- Ответ совпадает с последовательной суммой при любой длине слайса и любом
workers, в том числе когда workers больше len(nums) и когда длина не
делится нацело. Пустой слайс — 0.
2. Собрать числа из канала.
// SumFromProducers запускает n горутин-производителей. Горутина i вызывает
// produce(i, out) и через out отдаёт сколько угодно чисел (канал она не закрывает).
// Функция возвращает сумму всех отправленных чисел, когда все производители завершились.
func SumFromProducers(n int, produce func(id int, out chan<- int)) int
- Учитывается каждое отправленное число, ни одно не теряется.
- Функция возвращается только после того, как все
produce вернулись, и
не зависает, сколько бы чисел они ни отправили. n <= 0 — 0.
- После возврата горутин не остаётся.
Оба варианта проходят go test -race.
Пример
Sum([]int{1, 2, 3, 4, 5, 6, 7}, 3) // 28: куски [1 2 3] [4 5] [6 7] или похожие
SumFromProducers(3, func(id int, out chan<- int) {
for i := 0; i < 10; i++ {
out <- id
}
}) // 0·10 + 1·10 + 2·10 = 30
Устно: как выбрать число горутин для Sum самостоятельно? Когда
параллельная сумма вообще быстрее последовательной, и где здесь подвох с
памятью (false sharing, пропускная способность)?