Гоуротины, каналы, не полностью отрабатывает, как разобраться?

Приветствую, только начал изучать go и сразу же завис на каналах и гоуротинах. Сразу приведу код

func main() {

	chanUrls := make(chan string, 100)
	chanRes := make(chan http.Response, 100)
	zipChan := make(chan Gallery, 100)
        
        for url := range Urls{
                chanUrls <- url
       }


	go httpGet(chanUrls, chanRes)
	go parser(chanRes, zipChan)

	for index := 0; index < 2; index++ {
		go zipMaker(zipChan)
	}

       defer close(zipChan)

	for range zipChan {
	}
}

func httpGet(chanUrls <-chan string, chanRes chan<- http.Response) {
      //получаем урл, отправляем response дальше
}

func parser(chanRes <-chan http.Response, zipChan chan<- Gallery) {
	for resp := range chanRes {
           //парсим данные, создаем структуру, все как положено и отправляем дальше
	   zipChan <- gallery
       }
}

func zipMaker(zipChan <-chan Gallery) {
	for gallery := range zipChan {
          //тут качаем картинки из урлов, которые есть в срезе gallery.imgUrls , архивируем
        }
}


Вроде все просто и понятно, если вместо реальной работы в функции zipMaker сделать просто fmt.Println(gallery), то все отрабатывает как и ожидается, но, как только начниаем делать работу, выполняется только два задания(или столько, сколько запущено гуротин zipMaker) , далее прсто останавливается все, без выхода из программы.

Подскажите пожалуйста, я понимаю что в очереди zipChan проблема и она отбрасывает все, т.к. две горотины заняты, но ведь она с буфером, что не так?
  • Вопрос задан
  • 193 просмотра
Решения вопроса 2
Ваша программа завершается до того, как zipMaker() успевает обработать все данные: функция main завершается сразу после того, как завершается выполнение цикла.

Есть несколько вариантов решения этой проблема, какой выбрать, зависит от задачи:
1) если у вас программа должна работать постоянно, то тогда можно просто залочить ей выполнение. Например, так:
func main() {
     // your code

    locker := make(chan struct{})
    <-locker
}

2) если же вам нужно обработать конечное количество изображений, то нужно переработать код. По моему мнению, не нужно вызывать httpGet() и parser() в горутинах. Лучше возвращать []Gallery, после чего проходиться по нему и вызывать горутину для каждого элемента. При этом нужно воспользоваться sync.WaitGroup. Код будет выглядеть примерно так:
package main

import (
    "log"
    "sync"
    "time"
)

// Для удобства
type Gallery int

func main() {
    // То, что должна вернуть функция parser()
    results := []Gallery{5, 6, 7, 8}

    var wg sync.WaitGroup
    for _, r := range results {
        wg.Add(1)
        go zipMaker(r, &wg)
    }
 
    log.Println("Wait")
    // Ждём выполнения горутин
    wg.Wait()
    // "Done" напечатается через 2 секунды после "Wait"
    log.Println("Done")
}

func zipMaker(g Gallery, wg *sync.WaitGroup) {
    defer wg.Done()
    // your code

    time.Sleep(time.Second * 2)
}


sync.WaitGroup позволяет ждать обработки всех данных. Подробнее можно почитать тут.
Ответ написан
У вас в конце range по каналу
for range zipChan {
}

он просто читает данные из канала zipChan и выкидывает их в мусорку. Поэтому вы видите, что обработались только несколько урлов, остальные выкинул в мусор этот цикл.
Потом всё зависает потому что в zipChan кончились данные и этот цикл вечно будет их ждать.
defer с закрытием канала там не сработает, ибо у вас не будет выхода из функции.
Вам надо использовать waitGroup как советует другой человек ))
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы