Creating channels of channels

The preferred and sanctioned way of managing concurrency and state is exclusively through channels.

We've demonstrated a few more complex types of channels, but we haven't looked at what can become a daunting but powerful implementation: channels of channels. This might at first sound like some unmanageable wormhole, but in some situations we want a concurrent action to generate more concurrent actions; thus, our goroutines should be capable of spawning their own.

As always, the way you manage this is through design while the actual code may simply be an aesthetic byproduct here. Building an application this way should make your code more concise and clean most of the time.

Let's revisit a previous example of an RSS feed reader to demonstrate how we could manage this, as shown in the following code:

package main

import (
 "fmt"
)

type master chan Item

var feedChannel chan master
var done chan bool

type Item struct {
 Url  string
 Data []byte
}
type Feed struct {
 Url   string
 Name  string
 Items []Item
}

var Feeds []Feed

func process(feedChannel *chan master, done *chan bool) {
 for _, i := range Feeds {
  fmt.Println("feed", i)
  item := Item{}
  item.Url = i.Url
  itemChannel := make(chan Item)
  *feedChannel <- itemChannel
  itemChannel <- item
 }
 *done <- true
}
func processItem(url string) {
 // deal with individual feed items here
 fmt.Println("Got url", url)
}

func main() {
 done := make(chan bool)
 Feeds = []Feed{Feed{Name: "New York Times", Url: "http://rss.nytimes.com/services/xml/rss/nyt/HomePage.xml"},
  Feed{Name: "Wall Street Journal", Url: "http://feeds.wsjonline.com/wsj/xml/rss/3_7011.xml"}}
 feedChannel := make(chan master)
 go func(done chan bool, feedChannel chan master) {
  for {
   select {
   case fc := <-feedChannel:
    select {
    case item := <-fc:
     processItem(item.Url)
    }
   default:
   }
  }
 }(done, feedChannel)
 go process(&feedChannel, &done)
 <-done
 fmt.Println("Done!")
}

Here, we manage feedChannel as a custom struct that is itself a channel for our Item type. This allows us to rely exclusively on channels for synchronization handled through a semaphore-esque construct.

If we want to look at another way of handling a lower-level synchronization, sync.atomic provides some simple iterative patterns that allow you to manage synchronization directly in memory.

As per Go's documentation, these operations require great care and are prone to data consistency errors, but if you need to touch memory directly, this is the way to do it. When we talk about advanced concurrency features, we'll utilize this package directly.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.91.47