質問

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.

役に立ちましたか?

解決

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.

他のヒント

You code is racy: You write to the same memory location from different, unsynchronized goroutines without any locking. The result is basically undefined. You must either a) make sure that all the goroutine writes after each other in a nice, ordered way, or b) protect each write by e.g. e mutex or c) use atomic operations.

If you write such code: Always try it under the race detector like $ go run -race main.go and fix all races.

A nice alternative to using channels in this case might be the sync/atomic package, which contains specifically functions for atomically incrementing/decrementing numbers.

You are spawning 500 or 1000 routines without synchronization among routines. This is creating a race condition, which makes the result un-predictable.

Imagine You are working in an office to account for expense balance for your boss.

Your boss was in a meeting with 1000 of his subordinates and in this meeting he said, "I would pay you all a bonus, so we will have to increment the record of our expense". He issued following command: i) Go to Nerve and ask him/ her what is the current expense balance ii) Call my secretary to ask how much bonus you would receive iii) Add your bonus as an expense to the additional expense balance. Do the math yourself. iv) Ask Nerve to write the new balance of expense on my authority.

All of the 1000 eager participants rushed to record their bonus and created a race condition.

Say 50 of the eager gophers hit Nerve at the same time (almost), they ask, i) "What is the current expense balance? -- Nerve says $1000 to all of those 50 gophers, as they asked at the same question at the same time(almost) when the balance was $1000. ii) The gophers then called secretary, how much bonus should be paid to me? Secretary answers, "just $1 to be fair" iii) Boos said do the math, they all calculates $1000+ $1 = $1001 should be the new cost balance for the company iv) They all will ask Nerve to put back $1001 to the balance.

You see the problem in the method of Eager gophers? There are $50 units of computation done, every time the added $1 to the existing balance, but the cost didn't increase by $50; only increased by $1.

Hope that clarifies the problem. Now for the solutions, other contributors gave very good solutions that would be sufficient I believe.

All of that approaches failed to me, noobie here. But i have found a better way http://play.golang.org/p/OcMsuUpv2g

I'm using sync package to solve that problem and wait for all goroutines to finish, without Sleep or Channel.

And don't forget to take a look at that awesome post http://devs.cloudimmunity.com/gotchas-and-common-mistakes-in-go-golang/

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top