0

I'm trying to do some stuff:

type Feed struct { title, descr, link string published time.Time } func main() { ar := make([]Feed, 0) for i := 0; i < 3; i++ { f: = new(Feed) // do some stuff with feed ar = append(ar, *f) } ch := make(chan Feed, 3) for _, i := range ar { go process(i, ch) } r :=0 for i := range ch { fmt.Println(i) r++ if r == 3 { close(ch) } } } func process(i Feed, ch chan Feed) { // do some stuff ch <- i } 

It seems that ar is unnecessary, but if it would be removed, last range would be forever. What i'm doing wrong?

Another question is - is that way of working with Go routines the right way?

5
  • This specific example could easily be done with a sync.WaitGroup without needing any channels. Channels are often used in provider-consumer type applications where you have one or more routines creating tasks, and one or more routines executing the tasks. So my question for clarification is this: does your code creating the Feeds take enough time for it to make sense to goroutines for both creation and consumption of the Feeds? Does the "do some stuff with Feed" actually take any time? Commented Jun 30, 2017 at 11:50
  • @RayfenWindspear thanks for reply, yes "do some stuff with Feed" can take many time, so i'm trying to get results from processing feeds asap, i don't want wait until all go routines would be finished Commented Jun 30, 2017 at 12:50
  • please paste the code you removed ar. Commented Jun 30, 2017 at 13:04
  • @mattn i don't have working example without ar, it's a problem, but i guess same code without ar is possible Commented Jun 30, 2017 at 13:08
  • @inJakuzi Yes, I said I want to see it. Commented Jun 30, 2017 at 13:11

1 Answer 1

2

Here is an example producer-consumer type. I only use the WaitGroup here so that the main goroutine wouldn't exit instantly. Theoretically your application could either wait, or do some other interesting stuff in the mean time.

Note that you could also use a buffered channel using c := make(chan(*Feed, n)) where n is the number you want buffered. Just be aware that in a typical producer-consumer scenario, there is sometimes a lot of resources allocated per job. So depending on that you could buffer just a few, or all of them if you wanted.

Without a buffered channel, it acts as a sync between the goroutines. Producers block at c <- waiting for a consumer's <- c to hand off to, so only one of each routine execute these lines at a time.

EDIT I added a pause before printing "started" to make the output less synchronized. It previously always output:

created started created started ... 

https://play.golang.org/p/FmWqegr-CR

package main import ( "fmt" "math/rand" "sync" "time" ) type Feed struct { title, descr, link string published time.Time } func CreateFeed() *Feed { r := rand.Int() % 500 time.Sleep(1000 + time.Duration(r)*time.Millisecond) fmt.Println("Feed created") return &Feed{ published: time.Now(), } } func UseFeed(f *Feed) { time.Sleep(100 * time.Millisecond) fmt.Println("Feed started") time.Sleep(1600 * time.Millisecond) fmt.Printf("Feed consumed: %s\n", f.published) } func main() { numFeeds := 10 var wg sync.WaitGroup wg.Add(10) c := make(chan (*Feed)) for i := 0; i < numFeeds; i++ { go func() { c <- CreateFeed() }() } for i := 0; i < numFeeds; i++ { go func() { f := <-c UseFeed(f) wg.Done() }() } wg.Wait() } 

I'm hoping this is what you are looking for.

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

3 Comments

Looks like what i actually need, thanks. One more question: is sync.WaitGroup main thing when using goroutines, i saw many examples with goroutines and WG together, but in golangtour there is no information about WG
@inJakuzi In my opinion, it's not really the right way and is kind of a cheater way to do this. Don't get me wrong, it definitely has its uses, but it kind of wastes the time of the main routine. I did it here to push my answer out more quickly, so I apologize for that. What I would normally do instead (given the assumption that the main routine continues on to use the results), would be to have the second loop insert into a results channel that the main thread would pull from. That way it could start as soon as one was done instead of wg.Wait()ing for the entire set of results.
There are a lot of subtle things to remember when dealing with concurrency, but the biggest one is obviously (and yet not so obvious sometimes) that want to be waiting a little as possible. Hence my aversion to wg.Wait().

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.