goroutines如何在多核处理器上运行

Ner*_*rve 13 parallel-processing go goroutine

我是Go语言的新手,所以如果我的问题非常基本,请原谅.我写了一个非常简单的代码:

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
            }()
      }
    }
Run Code Online (Sandbox Code Playgroud)

count的值应该增加一个循环运行的次数.考虑案例:

循环运行100次 - >计数值为100(循环运行100次时这是正确的).

循环运行> 510次 - >计数值为508或510.即使它是100000也会发生.

我在8核处理器机器上运行它.

Vol*_*ker 12

你的代码是活泼的:你从不同的,不同步的goroutine写入相同的内存位置,没有任何锁定.结果基本上是未定义的.您必须a)确保所有goroutine以一种漂亮,有序的方式相互写入,或者b)通过例如e mutex或c)保护每次写入使用原子操作.

如果您编写此类代码:请始终在竞赛检测器下尝试$ go run -race main.go并修复所有比赛.

  • 没有理由,只是机会。 (2认同)

cfs*_*ras 11

首先:在Go 1.5之前,它在单个处理器上运行,仅使用多个线程来阻止系统调用.除非您通过使用GOMAXPROCS告诉运行时使用更多处理器.

从Go 1.5开始,GOMAXPROCS被设置为CPUS的数量.见6,7.

此外,操作*cptr = *cptr + 1不保证是原子的.如果仔细观察,可以将其拆分为3个操作:通过解除引用指针获取旧值,增加值,将值保存到指针地址.

你获得508/510的事实是由于运行时的一些魔力并没有被定义为保持这种状态.有关并发操作行为的更多信息可以在Go内存模型中找到.
您可能正在为<510启动的goroutine获取正确的值,因为这些下面的任何数字都没有(但)被中断.

一般来说,您尝试做的事情既不推荐任何语言,也不推荐使用"Go"方式进行并发.使用通道进行同步的一个非常好的示例是此代码遍历:通过通信共享内存(而不是通过共享内存进行通信)

这里有一个小例子向您展示我的意思:使用缓冲区为1的通道存储当前数字,在需要时从通道中获取它,随意更改,然后将其放回供其他人使用.