How does goroutines behave on a multi-core processor

Nerve picture Nerve · Jan 8, 2014 · Viewed 11.1k times · Source

I am a newbie in Go language, so please excuse me if my question is very basic. I have written a very simple code:

func main(){
    var count int // Default 0

    cptr := &count

    go incr(cptr)

    time.Sleep(100)

    fmt.Println(*cptr)
}

// Increments the value of count through pointer var
func incr(cptr *int) {
    for i := 0; i < 1000; i++ {
            go func() {
                    fmt.Println(*cptr)
                    *cptr = *cptr + 1
            }()
      }
    }

The value of count should increment by one the number of times the loop runs. Consider the cases:

Loop runs for 100 times--> value of count is 100 (Which is correct as the loop runs 100 times).

Loop runs for >510 times --> Value of count is either 508 OR 510. This happens even if it is 100000.

I am running this on an 8 core processor machine.

Answer

cfstras picture cfstras · Jan 8, 2014

First of all: prior to Go 1.5 it runs on a single processor, only using multiple threads for blocking system calls. Unless you tell the runtime to use more processors by using GOMAXPROCS.

As of Go 1.5 GOMAXPROCS is set to the number of CPUS. See 6, 7 .

Also, the operation *cptr = *cptr + 1 is not guaranteed to be atomic. If you look carefully, it can be split up into 3 operations: fetch old value by dereferencing pointer, increment value, save value into pointer address.

The fact that you're getting 508/510 is due to some magic in the runtime and not defined to stay that way. More information on the behaviour of operations with concurrency can be found in the Go memory model.
You're probably getting the correct values for <510 started goroutines because any number below these are not (yet) getting interrupted.

Generally, what you're trying to do is neither recommendable in any language, nor the "Go" way to do concurrency. A very good example of using channels to synchronize is this code walk: Share Memory By Communicating (rather than communicating by sharing memory)

Here is a little example to show you what I mean: use a channel with a buffer of 1 to store the current number, fetch it from the channel when you need it, change it at will, then put it back for others to use.