Understanding Deadlocks when using sync.WaitGroup and Channels in Go
Developers often encounter issues with their Go applications never exiting when utilizing sync.WaitGroup and channels. This article explores the reasons behind such deadlocks and offers a solution using the WaitGroup.
Consider the example code provided:
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 }
In this code, the range loop over fetchedSymbols will block the main function indefinitely. Why? Because the fetchedSymbols channel is never closed. To resolve this deadlock, the WaitGroup can be used to signal when to close the channel:
... go func() { wg.Wait() close(fetchedSymbols) }() for response := range fetchedSymbols { fmt.Println("fetched " + response) } ...
As the WaitGroup already tracks when all goroutines have completed, it can be leveraged to trigger the closing of the fetchedSymbols channel, ensuring the range loop terminates gracefully.
The above is the detailed content of Why Does a `sync.WaitGroup` and Channel Combination Lead to a Deadlock in Go?. For more information, please follow other related articles on the PHP Chinese website!