简体   繁体   English

goroutines 在多核处理器上的表现如何

[英]How does goroutines behave on a multi-core processor

I am a newbie in Go language, so please excuse me if my question is very basic.我是 Go 语言的新手,所以如果我的问题很基本,请原谅。 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. count 的值应按循环运行的次数递增 1。 Consider the cases:考虑以下情况:

Loop runs for 100 times--> value of count is 100 (Which is correct as the loop runs 100 times).循环运行 100 次--> 计数值为 100(循环运行 100 次时这是正确的)。

Loop runs for >510 times --> Value of count is either 508 OR 510. This happens even if it is 100000.循环运行 >510 次 --> 计数值是 508 或 510。即使是 100000,也会发生这种情况。

I am running this on an 8 core processor machine.我在 8 核处理器机器上运行它。

First of all: prior to Go 1.5 it runs on a single processor, only using multiple threads for blocking system calls.首先:在 Go 1.5 之前,它运行在单个处理器上,仅使用多个线程来阻塞系统调用。 Unless you tell the runtime to use more processors by using GOMAXPROCS .除非您通过使用GOMAXPROCS告诉运行时使用更多处理器。

As of Go 1.5 GOMAXPROCS is set to the number of CPUS.从 Go 1.5 开始,GOMAXPROCS 被设置为 CPU 的数量。 See 6 , 7 .67

Also, the operation *cptr = *cptr + 1 is not guaranteed to be atomic.此外,操作*cptr = *cptr + 1不能保证是原子的。 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.如果仔细观察,它可以分为 3 个操作:通过取消引用指针获取旧值、增加值、将值保存到指针地址。

The fact that you're getting 508/510 is due to some magic in the runtime and not defined to stay that way.您获得 508/510 的事实是由于运行时中的一些魔法,而不是定义为保持这种状态。 More information on the behaviour of operations with concurrency can be found in the Go memory model .更多关于并发操作行为的信息可以在Go 内存模型中找到
You're probably getting the correct values for <510 started goroutines because any number below these are not (yet) getting interrupted.您可能会获得 <510 启动的 goroutines 的正确值,因为低于这些值的任何数字(尚未)被中断。

Generally, what you're trying to do is neither recommendable in any language, nor the "Go" way to do concurrency.通常,您尝试做的事情在任何语言中都不值得推荐,也不是进行并发的“Go”方式。 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.这里有一个小例子来向你展示我的意思:使用一个缓冲区为 1 的通道来存储当前数字,在你需要的时候从通道中获取它,随意更改它,然后把它放回去供其他人使用。

You code is racy: You write to the same memory location from different, unsynchronized goroutines without any locking.你的代码很活泼:你从不同的、不同步的 goroutines 写入相同的内存位置而没有任何锁定。 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 eg e mutex or c) use atomic operations.您必须要么 a) 确保所有 goroutine 以良好、有序的方式依次写入,或 b) 通过例如 e mutex 或 c) 使用原子操作保护每次写入。

If you write such code: Always try it under the race detector like $ go run -race main.go and fix all races.如果你写这样的代码:总是在种族检测器下尝试它,比如$ go run -race main.go并修复所有种族。

在这种情况下使用通道的一个很好的替代方法可能是sync/atomic包,它包含专门用于原子递增/递减数字的函数。

You are spawning 500 or 1000 routines without synchronization among routines.您正在生成 500 或 1000 个例程,而例程之间没有同步。 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".你的老板和他的 1000 名下属开会,在这次会议上他说:“我会给你们一个奖金,所以我们必须增加我们的费用记录”。 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.他发出了以下命令: i) 去 Nerve 询问他/她当前的费用余额是多少 ii) 打电话给我的秘书询问您将获得多少奖金 iii) 将您的奖金作为费用添加到额外费用余额中。 Do the math yourself.自己算一下。 iv) Ask Nerve to write the new balance of expense on my authority. iv) 请 Nerve 在我的授权下写出新的费用余额。

All of the 1000 eager participants rushed to record their bonus and created a race condition.所有 1000 名热切的参赛者都争先恐后地记录他们的奖金并创造了比赛条件。

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.假设有 50 个热心的地鼠同时(几乎)击中了 Nerve,他们会问,i)“目前的费用余额是多少?- Nerve 对所有这 50 个地鼠说 1000 美元,因为他们在同一时间(几乎)当余额为 1000 美元时。 ii) 然后地鼠打电话给秘书,应该给我多少奖金?秘书回答,“公平地说,只需 1 美元” iii) Boos 说你算一算,他们都计算了 1000 美元+ $1 = $1001 应该是公司的新成本余额 iv) 他们都会要求 Nerve 将 1001 美元退回到余额中。

You see the problem in the method of Eager gophers?您看到 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;完成了 50 美元的计算单元,每次在现有余额中增加 1 美元,但成本并没有增加 50 美元; only increased by $1.仅增加了 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 .所有这些方法对我来说都失败了, noobie here But i have found a better way http://play.golang.org/p/OcMsuUpv2g但我找到了更好的方法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.我正在使用同步包来解决该问题并等待所有 goroutine 完成,而没有 Sleep 或 Channel。

And don't forget to take a look at that awesome post http://devs.cloudimmunity.com/gotchas-and-common-mistakes-in-go-golang/并且不要忘记看看那个很棒的帖子http://devs.cloudimmunity.com/gotchas-and-common-mistakes-in-go-golang/

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM