Задать вопрос

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

Приветствую, только начал изучать 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 проблема и она отбрасывает все, т.к. две горотины заняты, но ведь она с буфером, что не так?
  • Вопрос задан
  • 202 просмотра
Подписаться 2 Простой 2 комментария
Решения вопроса 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 как советует другой человек ))
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

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

Похожие вопросы