Тупик в диапазоне по каналу в конвейере с несколькими группами ожидания

Я отрабатываю задачу по вычислению факториалов путем одновременного разделения вычислений на 100 групп, я решил много проблем с WaitGroups, но все же в функции calculateFactorial я получил взаимоблокировку по диапазону по части канала. Хотелось бы, чтобы кто-нибудь мог указать здесь на проблему, спасибо.

package main import ( «fmt» «sync» ) func main() { var wg sync.WaitGroup wg.Add(2) in := make (chan int) out := make (chan float64) out = calculateFactorial(genConcurrentGroup(in, &wg), &wg) go func() { in <- 10 close(in) }() fmt.Println(<-out) wg.Wait() } //split input number into groups //the result should be a map of [start number, number in group] //this is not heavy task so run in one go routine func genConcurrentGroup(c chan int, wg *sync.WaitGroup) chan map[int]int{ out := make(chan map[int]int) go func() { //100 groups total:= <- c wg.Done() //element number in group elemNumber := total / 100 extra := total % 100 result := make(map[int]int) if elemNumber>0{ //certain 100 groups for i:=1 ;i<=99;i++{ result[(i-1) * elemNumber + 1] = elemNumber } result[100] = extra + elemNumber }else{ //less than 100 for i:=1;i<=total;i++{ result[i] = 1 } } out <- result close(out) }() return out } //takes in all numbers to calculate multiply result //this could be heavy so can do it 100 groups together func calculateFactorial(nums chan map[int]int, wg *sync.WaitGroup) chan float64{ out := make(chan float64) go func() { total:= <- nums wg.Done() fmt.Println(total) oneResult := make(chan float64) var wg2 sync.WaitGroup wg2.Add(len(total)) for k,v := range total{ fmt.Printf(«%d %d n»,k,v) go func(k int, v int) { t := 1.0 for i:=0;i<v;i++{ t = t * (float64(k) + float64(i)) } fmt.Println(t) oneResult <- t wg2.Done() }(k,v) } wg2.Wait() close(oneResult) result := 1.0 for n := range oneResult{ //DEADLOCK HERE! Why? result *= n } fmt.Printf(«Result: %fn»,result) out <- result }() return out }

Обновление:

Благодаря ответу Джессе Катринка, который устранил проблему в приведенном выше коде, просто изменив oneResult на буферный канал. Однако в https://stackoverflow.com/a/15144455/921082 есть цитата

Никогда не следует добавлять буферизацию только для устранения тупика. Если ваша программа заходит в тупик, гораздо проще исправить это, начав с нулевой буферизации и продумав зависимости. Затем добавьте буферизацию, если знаете, что она не приведет к взаимоблокировке.

Так может ли кто-нибудь помочь мне выяснить, как не использовать для этого буферизованный канал? Является ли это возможным?

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

Некоторые цитаты, например, из https://stackoverflow.com/a/18660709/921082,

Если канал не буферизован, отправитель блокируется до тех пор, пока получатель не получит значение. Если канал имеет буфер, отправитель блокируется только до тех пор, пока значение не будет скопировано в буфер; если буфер заполнен, это означает ожидание, пока какой-либо получатель не получит значение.

Сказано иначе:

  1. когда канал заполнен, отправитель ждет, пока другая горутина освободит место, получив

  2. вы можете видеть небуферизованный канал как всегда заполненный: должна быть другая горутина, принимающая то, что отправитель отправляет.

Итак, в моей исходной ситуации тупик, вероятно, вызван следующими причинами:

  1. диапазон по каналу не принимается?

  2. диапазон по каналу не принимается в отдельной программе. ?

  3. oneResult не закрыт должным образом, поэтому диапазон по каналу не знает, где конец?

для номера 3 я не знаю, есть ли что-нибудь неправильное в закрытии oneResult перед выходом диапазона, поскольку этот шаблон встречается во многих примерах в Интернете. Если это номер 3, может быть, что-то не так в группе ожидания?

У меня есть еще одна статья, очень похожая на мою ситуацию https://robertbasic.com/blog/buffered-vs-unbuffered-channels-in-golang/, на втором усвоенном уроке он использует for { select {} } бесконечный цикл в качестве альтернативы расширению диапазона, похоже, это решило его проблему.

go func() { for{ select { case p := <-pch: findcp(p) } } }()

Урок номер 2 — небуферизованный канал не может удерживать значения (да, это прямо в названии «небуферизованный»), поэтому все, что отправляется на этот канал, должно быть немедленно получено каким-то другим кодом. Этот код приема должен быть в другой горутине, потому что одна горутина не может делать две вещи одновременно: она не может отправлять и получать; это должно быть одно или другое.

Спасибо

Источник: ledsshop.ru

Стиль жизни - Здоровье!