Golang concurrent array access

11,957

Solution 1

As long as you can guarantee the areas won't overlap, it's fine.

By guarantee I mean: whomever works on sliceA, should not be allowed to do sliceA = append(sliceA, a, b, c). Because then it'll start running into sliceB's territory.

Relevant here, is some documentation for Go 1.2: This concerns a new language element: 3-index slices:

Go 1.2 adds the ability to specify the capacity as well as the length when using a slicing operation on an existing array or slice. A slicing operation creates a new slice by describing a contiguous section of an already-created array or slice:

var array [10]int
slice := array[2:4]

The capacity of the slice is the maximum number of elements that the slice may hold, even after reslicing; it reflects the size of the underlying array. In this example, the capacity of the slice variable is 8.

Go 1.2 adds new syntax to allow a slicing operation to specify the capacity as well as the length. A second colon introduces the capacity value, which must be less than or equal to the capacity of the source slice or array, adjusted for the origin. For instance,

slice = array[2:4:7]

sets the slice to have the same length as in the earlier example but its capacity is now only 5 elements (7-2). It is impossible to use this new slice value to access the last three elements of the original array.

In this three-index notation, a missing first index ([:i:j]) defaults to zero but the other two indices must always be specified explicitly. It is possible that future releases of Go may introduce default values for these indices.

Further details are in the design document.

Solution 2

Actually jimt's answer MAY be wrong. It depends... :)

E.g. if you are using a []uint8, then a operation like

p[2] = 5

is essentially this

tmp = p[0..3] // this is 32 bit
tmp[2] = 5
p[0..3] = tmp // yeah this is all fake syntax but you'll get it

This is because your CPU is 32 (or even 64) bit. So that is actually more efficient although it seems more complex.

But as you can see, you are WRITING p[0,1,3] although you only intended to write to p[2]. This can create some fun bugs to debug! :)

If your data is e.g. pointers to your data then this issue should not occur as arrays are guaranteed to be stored in memory so that this problem doesn't occur as long as your data is as long as your native instruction set.

Share:
11,957
Kr0e
Author by

Kr0e

Updated on June 04, 2022

Comments

  • Kr0e
    Kr0e almost 2 years

    Is it safe to access the same array from multiple goroutines, when every goroutine works on a slice, pointing to the same underlying array but without overlapping ?

    Like:

    var arr [100]int
    sliceA := arr[:50]
    sliceB := arr[50:]
    
    go WorkOn(sliceA)
    go WorkOn(sliceB)
    

    Just imagine "WorkOn" would do something fancy.

  • Kr0e
    Kr0e about 10 years
    Thx, my concerns were that this must be guarded by a mutex since it's a single array. Good to hear that this is safe. From Java's memory model I know the differences between cache flush/refresh and the effects for inter-thread visibility. Since Go does permit sharing memory via goroutines this question came straight to my head. Though I'm not quite sure who this is done. Is the Go-Runtime able to flush certain sections of a single array ? I should really take a deep look into the implementation by time.
  • Rick-777
    Rick-777 about 10 years
    One of the concerns over Go's concurrency model is that it offers no protection against parallel usage of shared data. That's ok - you the programmer take the risk. Ensuring there are no accidental race conditions because of shared memory is your job. Occam was a language that took the opposite stance, providing anti-aliasing rules in the language syntax and parallel usage checking in the compiler. This was very sophisticated; maybe one day Go will have something similar.
  • Intermernet
    Intermernet about 10 years
    @Rick-777 Possibly not in the same league as Occam, but Go does have a wonderful race detector that can help prevent the pitfalls of unsafe concurrent access. golang.org/doc/articles/race_detector.html
  • Rick-777
    Rick-777 about 10 years
    Yes Go has a useful runtime race analyser, which will help with solving the question posed above. (Occam's race analyser is a static compile-time thing.)
  • Martin Rauscher
    Martin Rauscher over 4 years
    why the downvote? Not nice without an explanatory comment... Especially as I had just that bug on ARM 32Bit...
  • erik258
    erik258 over 4 years
    the answer's a bit hard to follow - I think I grok what you're saying only because I have vague experience with memory barriers. But I think the point is, don't make assumptions about the atomicity of your operations.
  • Kyle
    Kyle over 3 years
    I think the problem is that you never specifically explain that a 32-bit computer accesses 32 bits of memory at a time, so if your slice element type is smaller than that you may actually be accessing and storing an off-limits part of the slice.