Тупик в диапазоне по каналу в конвейере с несколькими группами ожидания - PullRequest
0 голосов
/ 26 февраля 2019

Я практикую задачу по вычислению факториалов путем одновременного разделения вычислений на 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,

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

Иначе:

  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 - небуферизованный канал не может удерживать значения (да, это прямо в названии «небуферизованный »), поэтому, что бы ни отправлялось на этот канал, оно должно быть сразу же получено другим кодом.Этот принимающий код должен быть в другой программе, потому что одна программа не может делать две вещи одновременно: она не может отправлять и получать;это должен быть один или другой.

Спасибо

Ответы [ 2 ]

0 голосов
/ 26 февраля 2019

Тупик не находится в контуре диапазона по каналу.Если вы запустите код на площадка , вы увидите в верхней части трассировки стека, что ошибка вызвана wg2.Wait (строка 88 на площадке и на нее указывает трассировка стека).Также в трассировке стека вы можете увидеть все процедуры, которые не были завершены из-за тупика, это потому, что oneResult<-t никогда не завершается, поэтому ни одна из процедур, начатых в цикле, никогда не заканчивается.

Так что основнойпроблема здесь:

wg2.Wait()
close(oneResult)

// ...

for n := range oneResult{
// ...

Также, я полагаю, циклы по закрытому каналу - это не то, что вы хотите.Однако, даже если вы не закрыли канал, этот цикл никогда не запустится, потому что wg2.Wait() будет ждать, пока его done .

oneResult <- t
wg2.Done()

Но этого никогда не будет сделано, потому что он полагаетсяна петле, которая уже работает.Линия oneResult <- t не будет завершена, пока на другой стороне не будет получен сигнал от этого канала, которым является ваш цикл, однако этот цикл диапазона по каналу все еще ожидает завершения wg2.Wait().

Итакпо существу, у вас есть «круговая зависимость» между отправителем и получателем канала.

Чтобы устранить эту проблему, вам нужно разрешить циклу начать прием с канала, при этом удостоверившись, что канал закрыт, когда закончите.Вы можете сделать это, обернув две строки ожидания и закрытия в свою собственную программу.

https://play.golang.com/p/rwwCFVszZ6Q

0 голосов
/ 26 февраля 2019

Вам необходимо добавить буфер к переменной oneResult

enter image description here

Объяснение: https://www.rapidloop.com/blog/golang-channels-tips-tricks.html

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