title: Go concurrent slice access
body: <p>I'm doing some stream processing in Go and got stuck trying to figure out how to do this the "Go way" without locks. </p>
  
  <p>This contrived example shows the problem I'm facing. </p>
  
  <ul>
  <li>We get one <code>thing</code> at a time.</li>
  <li>There is a goroutine which buffers them into a slice called <code>things</code>. </li>
  <li>When <code>things</code> becomes full <code>len(things) == 100</code> then it is processed somehow and reset</li>
  <li>There are <code>n</code> number of concurrent goroutines that need to access <code>things</code> before it's full</li>
  <li>Access to the "incomplete" <code>things</code> from other goroutines is not predictable.</li>
  <li>Neither <code>doSomethingWithPartial</code> nor <code>doSomethingWithComplete</code> needs to mutate <code>things</code> </li>
  </ul>
  
  <p>Code: </p>
  
  <pre><code>var m sync.Mutex
  var count int64
  things := make([]int64, 0, 100)
  
  // slices of data are constantly being generated and used
  go func() {
    for {
      m.Lock()
      if len(things) == 100 {
        // doSomethingWithComplete does not modify things
        doSomethingWithComplete(things)
        things = make([]int64, 0, 100)
      }
      things = append(things, count)
      m.Unlock()
      count++
    }
  }()
  
  // doSomethingWithPartial needs to access the things before they're ready
  for {
    m.Lock()
    // doSomethingWithPartial does not modify things
    doSomethingWithPartial(things)
    m.Unlock()
  }
  </code></pre>
  
  <ol>
  <li><p><s>I know that slices are immutable so does that mean I can remove the mutex and expect it to still work (I assume no)</s>.</p></li>
  <li><p>How can I refactor this to use channels instead of a mutex.</p></li>
  </ol>
  
  <p><strong>Edit:</strong> Here's the solution I came up with that does not use a mutex</p>
  
  <pre><code>package main
  
  import (
      "fmt"
      "sync"
      "time"
  )
  
  func Incrementor() chan int {
      ch := make(chan int)
      go func() {
          count := 0
          for {
              ch &lt;- count
              count++
          }
      }()
      return ch
  }
  
  type Foo struct {
      things   []int
      requests chan chan []int
      stream   chan int
      C        chan []int
  }
  
  func NewFoo() *Foo {
      foo := &amp;Foo{
          things:   make([]int, 0, 100),
          requests: make(chan chan []int),
          stream:   Incrementor(),
          C:        make(chan []int),
      }
      go foo.Launch()
      return foo
  }
  
  func (f *Foo) Launch() {
      for {
          select {
          case ch := &lt;-f.requests:
              ch &lt;- f.things
          case thing := &lt;-f.stream:
              if len(f.things) == 100 {
                  f.C &lt;- f.things
                  f.things = make([]int, 0, 100)
              }
              f.things = append(f.things, thing)
          }
      }
  }
  
  func (f *Foo) Things() []int {
      ch := make(chan []int)
      f.requests &lt;- ch
      return &lt;-ch
  }
  
  func main() {
  
      foo := NewFoo()
  
      var wg sync.WaitGroup
      wg.Add(10)
  
      for i := 0; i &lt; 10; i++ {
          go func(i int) {
              time.Sleep(time.Millisecond * time.Duration(i) * 100)
              things := foo.Things()
              fmt.Println("got things:", len(things))
              wg.Done()
          }(i)
      }
  
      go func() {
          for _ = range foo.C {
              // do something with things
          }
      }()
  
      wg.Wait()
  }
  </code></pre>