Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is is safe to append() to a slice from which another thread is reading?

Tags:

go

Let's say I have many goroutines doing something like this:

func (o *Obj) Reader() {
  data := o.data;
  for i, value := range data {
    log.Printf("got data[%v] = %v", i, value)
  }
}

And one doing this:

func (o *Obj) Writer() {
    o.data = append(o.data, 1234)
}

If data := o.data means the internal structure of the slice is copied, this looks like it could be safe, because I'm never modifying anything in the accessible range of the copy. I'm either setting one element outside of the range and increasing the length, or allocating a completely new pointer, but the reader would be operating on the original one.

Are my assumptions correct and this is safe to do?

I'm aware that slices are not meant to be "thread-safe" in general, the question is more about how much does slice1 := slice2 actually copy.

like image 396
Lukáš Lalinský Avatar asked Oct 27 '16 21:10

Lukáš Lalinský


Video Answer


1 Answers

The code in the question is unsafe because it reads a variable in one goroutine and modifies the variable in another goroutine without synchronization.

Here's one way to make the code safe:

type Obj struct {
   mu sync.Mutex // add mutex
   ... // other fields as before
}

func (o *Obj) Reader() {
    o.mu.Lock()
    data := o.data
    o.mu.Unlock()
    for i, value := range data {
      log.Printf("got data[%v] = %v", i, value)
    }
}

func (o *Obj) Writer() {
     o.mu.Lock()
     o.data = append(o.data, 1234)
     o.mu.Unlock()
}

It's safe for Reader to range over the local slice variable data because the Writer does not modify the local variable data or the backing array visible through the local variable data.

like image 85
Bayta Darell Avatar answered Oct 02 '22 14:10

Bayta Darell