Я практикую задачу по вычислению факториалов путем одновременного разделения вычислений на 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: %f\n",result)
out <- result
}()
return out
}
Обновление :
Благодаря ответу Джесси Катринка, который исправил проблему в приведенном выше коде, просто изменивoneResult
для буферизованного канала.Однако в https://stackoverflow.com/a/15144455/921082 есть цитата
Никогда не следует добавлять буферизацию просто для устранения тупика.Если ваша программа блокируется, ее гораздо проще исправить, начав с нулевой буферизации и продумав зависимости.Затем добавьте буферизацию, когда вы знаете, что она не заходит в тупик.
Так может ли кто-нибудь помочь мне разобраться, как не использовать буферизованный канал для этого?Возможно ли это?
Кроме того, я провел некоторое исследование о том, что именно вызывает тупик.
Некоторые цитаты, такие как https://stackoverflow.com/a/18660709/921082,
Если канал небуферизован, отправитель блокируется, пока получатель не получит значение.Если у канала есть буфер, отправитель блокируется только до тех пор, пока значение не будет скопировано в буфер;если буфер заполнен, это означает ожидание, пока какой-либо получатель не получит значение.
Иначе:
когда канал заполнен, отправитель ожидает другую процедуручтобы освободить место, получив
, вы можете видеть небуферизованный канал как всегда полный: должен быть другой режим, чтобы принять то, что отправляет отправитель.
Так что в моей исходной ситуации, вероятно, причина тупика может быть:
диапазон по каналу не получает?
диапазон по каналу не принимается в режиме раздельного пуска.?
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 - небуферизованный канал не может удерживать значения (да, это прямо в названии «небуферизованный »), поэтому, что бы ни отправлялось на этот канал, оно должно быть сразу же получено другим кодом.Этот принимающий код должен быть в другой программе, потому что одна программа не может делать две вещи одновременно: она не может отправлять и получать;это должен быть один или другой.
Спасибо