Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Golang app using sync.WaitGroup & channels never exits

Tags:

go

goroutine

I use sync.WaitGroup, defer wg.Close() and wg.Wait() to wait for my goroutines to complete.

The program do wait, but it never exits.

This is my program (runnable):

package main

import (
    "fmt"
    "io"
    "log"
    "net/http"
    "os"
    "sync"
)

var symbols = []string{
    "ASSA-B.ST",
    "ELUX-B.ST",
    "HM-B.ST",
}

func main() {

    fmt.Println("fetching quotes...")

    fetchedSymbols := make(chan string)
    var wg sync.WaitGroup
    wg.Add(len(symbols))

    for _, symbol := range symbols {
        go fetchSymbol(symbol, &wg, fetchedSymbols)
    }

    for response := range fetchedSymbols {
        fmt.Println("fetched " + response)
    }

    wg.Wait()

    fmt.Println("done")

}

func fetchSymbol(symbol string, wg *sync.WaitGroup, c chan<- string) {
    defer wg.Done()
    resp, err := http.Get("http://ichart.yahoo.com/table.csv?s=" + symbol + "&a=0&b=1&c=2000")
    defer resp.Body.Close()

    if err != nil {
        log.Fatal(err)
    }

    out, err := os.Create("./stock-quotes/" + symbol + ".csv")
    defer out.Close()

    if err != nil {
        log.Fatal(err)
    }

    io.Copy(out, resp.Body)
    c <- symbol
}

Shouldn't this program exit when all the quotes have been downloaded? (FYI: I just started learning GO)

like image 271
Mickel Avatar asked Dec 14 '15 22:12

Mickel


1 Answers

You're never closing the fetchedSymbols channel, so that range loop will never exit.

One way to handle this is to use use the WaitGroup you already have to signal when to close the channel. Ranging over fetchedSymbols is enough to block the progress in main, and you don't need another channel or WaitGroup.

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

for response := range fetchedSymbols {
    fmt.Println("fetched " + response)
}

...
like image 83
JimB Avatar answered Sep 20 '22 17:09

JimB