Тупик с множественными горутинами и одним каналом - PullRequest
0 голосов
/ 30 декабря 2018

У меня проблема тупика, которую я не могу решить.

У меня есть несколько URL-адресов, и каждый URL-адрес в goroutine принесет мне достаточно данных.Эти данные я выкладываю в один канал.Но если я закрываю канал, программа не работает, если я оставляю канал открытым, появляется тупик.

Я не знаю, как решить, у кого-то будет решение

Ниже я ставлюзадача в упрощенном виде

package main

import (
    "fmt"
)

type urlNumbers struct {
    url string
    numbers []int
}

func getNumbers(urls []urlNumbers) chan int {
    ch := make(chan int)

    for _, url := range urls {
        go allNumbersOfURL(url, ch)
    }

    return ch
}

func allNumbersOfURL(url urlNumbers, ch chan int) {
    for _, i := range url.numbers {
        ch <- i
    }
}

func main() {
    url1 := urlNumbers {url: "1", numbers: []int{1, 2, 3}}
    url2 := urlNumbers {url: "2", numbers: []int{4, 5, 6}}
    url3 := urlNumbers {url: "3", numbers: []int{7, 8, 9}}
    url4 := urlNumbers {url: "4", numbers: []int{10, 11, 12}}

    c := getNumbers([]urlNumbers{url1, url2, url3, url4})

    for i := range c {
        fmt.Println(i)
    }

    fmt.Println("END")

}

Вывод

go run app.go
10
11
12
4
7
1
2
3
5
6
8
9
fatal error: all goroutines are asleep - deadlock!

Ответы [ 2 ]

0 голосов
/ 30 декабря 2018

канал должен быть закрыт, вы можете использовать sync.WaitGroup для ожидания выполненных задач.Вот модификация функции getNumbers

func getNumbers(urls []urlNumbers) <-chan int {
    ch := make(chan int)

    wg := &sync.WaitGroup{}
    for _, url := range urls {
        wg.Add(1)
        go func(url urlNumbers, ch chan<- int) {
            defer wg.Done()
            allNumbersOfURL(url, ch)
        }(url, ch)
    }

    go func(wg *sync.WaitGroup, ch chan int) {
        wg.Wait()
        close(ch)
    }(wg, ch)

    return ch
}

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

Необязательный оператор <- указывает направление канала, отправлять илиПолучать.Если направление не указано, канал является двунаправленным. </p>

0 голосов
/ 30 декабря 2018

Вы используете for i := range c для итерации по каналу, но код не знает, когда остановиться.range по каналу ждет, пока канал не закроется, или навсегда зависнет.Вот почему существует тупик.

Вы должны закрыть свой канал после того, как все «URL» будут переданы по каналу.Это может быть достигнуто с помощью sync.WaitGroupgetNumbers перед циклом вы можете использовать WaitGroup и задать число ожидающих заданий как len(urls):

wg:=&sync.WaitGroup{}
wg.Add(len(urls))

И добавить новую программу перед возвратом ch:

go func() {
    wg.Wait()
    close(ch)
} ()

А затем в allNumbersOfURL добавьте WaitGroup в качестве нового параметра и установите задание, выполненное после цикла.

func allNumbersOfURL(url urlNumbers, ch chan int,wg *sync.WaitGroup) {
    for _, i := range url.numbers {
        ch <- i
    }
    wg.Done()
}

Playground: https://play.golang.org/p/--7x7eXIzP9

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