Hi! 

I find myself often writing tools that follow the pattern in the
subject, that is:

- Gather some input to process, e.g. a list of filenames from the
  command line. If this can be streamed (processing starting before
  knowing all inputs) even better, but not strictly necessary.
- Start N workers that process several of the inputs. They receive them
  through an appropriate channel, e.g. `chan string` for filenames. The
  results of the processing are put into another channel, typically some
  custom struct. When the input channel is closed the worker exits.
- After starting all the workers, the main goroutine collects the
  results from the results channel (and accumulates them in a slice if
  they need to be sorted, or just prints them as needed etc).

As code (I've omitted a few things for brevity):

func process(filenames []string) {
      results := make([]imageInfo, 0, len(filenames))
      wc := make(chan string) // work
      rc := make(chan imageInfo) // results
      numprocs := maxParallelism()
      for i := 0; i <= numprocs; i++ {
          go worker(wc, rc)
      }
      go func(fns []string) {
          for _, fn := range fns {
              wc <- fn
          }
          close(wc)
      }(filenames)
      for i := 0; i < len(filenames); i++ {
          results = append(results, <-rc)
      }
      sort.Slice(results, func(x, y int) bool { return results[x].name < 
results[y].name })
      for _, r := range results {
          fmt.Printf("%s %d %d %.2f %t %t\n", r.name, r.width, r.height)
      }
  }

  func worker(wc chan string, rc chan imageInfo) {
      for fn := range wc {
          // mustLoadImage just wraps functions from image/png etc
          rc <- mustLoadImage(fn)
      }
  }

  What I wonder about is the collection stage: the assumption that there
  will be exactly as many results as inputs seems brittle, and the only
  real way to handle errors is to put them into the result struct and
  examining them later.

  I also usually prefer handling workers with a `waitgroup`, but that only
  really works if there is no return channel to handle, either because
  the workers do the output/final processing, or because results are
  written to a global. The former is not always possible, and the latter
  is ugly.

  There is also the option of just making the result channel big enough
  to hold all results, but that seems a bit ugly as well.

  So what's the most idiomatic way of doing this?

  Best,
  Tobias

-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/golang-nuts/d32af78b-2d06-40cb-8ba8-39a8be494968%40skade.local.

Reply via email to