Search code examples
concurrencythread-safetygoatomic

Are increment operators in Go atomic on x86?


Here's some background:
I need a counter variable shared between go routines, used for something like a leaky-bucket. I know there's an example of leaky-bucket in Effective Go, concurrent section, but the number that I need to track may be very large, and I feel it's inefficient to use number of elements in a channel to track it. So I am considering of using a shared variable between different routines to track the number.

I understand that without explicit configuration, all go routines are mapped onto one thread. But if I assign more than one thread to the program on a multi-core computer, is increment operators atomic? Is it all the same for different data types (int32, float32, etc.) on different machines (x86_32, x86_64, arm)?

To be more concrete, What if I have counter += 1000 in one routine, and counter -= 512 in another routine, and the two routines happens to be running in two threads? Do I need to worry about thread-safty? Shall I put on locks on the counter?


Solution

  • No, increment should never be assumed to be atomic. Use the atomic addition functions or a mutex.

    Lets assume:

    import "sync/atomic"
    var counter = new(int32)
    

    One goroutine could do atomic.AddInt32(counter, 1000) while another did atomic.AddInt32(counter, -512) without a mutex.

    If you would rather use a mutex:

    import "sync"
    var counter int32
    var mutex sync.Mutex
    
    func Add(x int32) {
        mutex.Lock()
        defer mutex.Unlock()
        counter += x
    }