2

I'm trying to port a simple synchronous bit of PHP to Go, but am having a hard time getting my head around how concurrency works with regards to channels. The PHP script makes a request to get a list of media library sections, then makes requests to get the items within each of these sections. If the section is a list of TV Shows, it then makes a request for each show to get all the seasons and then another to get the episodes within each season.

I've trying writing in pidgeon-go what I expected to work, but I'm not having any luck. I've tried various channel guides online, but normally end up with deadlock warnings. Currently this example warns about item := <-ch used as value and doesn't look like it's waiting on the goroutines to return. Does anyone have any ideas what I can do?

package main

import (
    "fmt"
    "time"
)

// Get all items for all sections
func main() {

    ch := make(chan string)
    sections := getSections()

    for _, section := range sections {
        go getItemsInSection(section, ch)
    }

    items := make([]string, 0)

    for item := <- ch {
        items = append(items, item)
    }

    fmt.Println(items)

}

// Return a list of the various library sections
func getSections() []string {

    return []string{"HD Movies", "Movies", "TV Shows"}

}

// Get items within the given section, note that some items may spawn sub-items
func getItemsInSection(name string, ch chan string) {

    time.Sleep(1 * time.Second)

    switch name {

    case "HD Movies":
        ch <- "Avatar"
        ch <- "Avengers"

    case "Movies":
        ch <- "Aliens"
        ch <- "Abyss"

    case "TV Shows":
        go getSubItemsForItem("24", ch)
        go getSubItemsForItem("Breaking Bad", ch)

    }

}

// Get sub-items for a given parent
func getSubItemsForItem(name string, ch chan string) {

    time.Sleep(1 * time.Second)

    ch <- name + ": S01E01"
    ch <- name + ": S01E02"

}

1 Answer 1

2

First, that code doesn't compile because for item := <- ch should be for item := range ch

Now the problem is you either have to close the channel or run your loop forever inside a goroutine.

go func() {
    for {
        item, ok := <-ch
        if !ok {
            break
        }
        fmt.Println(item)
        items = append(items, item)

    }
}()
time.Sleep(time.Second)
fmt.Println(items)

playground

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks. What's the best way to wait and close the channel when it is all complete? The requests can take an unpredictable amount of time. I've tried a WaitGroup, but it warns that all goroutines are asleep.
I figured it out, I just had to move the WaitGroup.Done() to after the listening goroutine. Thanks for your help.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.