Concurrency in golang

huangapple go评论72阅读模式
英文:

Concurrency in golang

问题

我对Go语言中的并发性有一个问题。这是一个Go语言的示例代码:

package main

import (
	"fmt"
	"time"
)

var m int

func add(i int) {
	m++
}

func main() {
	m = 0
	for i := 0; i < 100; i++ {
		go add(i)
	}
	time.Sleep(time.Millisecond * 1000)
	fmt.Println(m)
}

当我执行它时,无论执行多少次,结果始终是100。

如果我在C中执行相同的代码(没有使用互斥锁),有时会得到不同的结果。

我的问题是,我想知道Go语言是否通过内部机制隐式地管理对共享变量的访问?

谢谢。

英文:

I have a question about concurrency in GoLang. Here is a sample code in GoLang

package main

import(
	&quot;fmt&quot;
	&quot;time&quot;
)

var m int
func add(i int){
	m++
}

func main() {
	m = 0
	for i:=0;i&lt;100;i++{
		go add(i)
	}
	time.Sleep(time.Millisecond * 1000)
	fmt.Println(m)
}

When I execute it I always have the same result 100, even if I execute it several times.

If I do the same code in C (without mutex), sometimes I have different results.

And my question, I would like to know if GoLang implicitly manages access to a shared variable using an internal mechanism ?

Thank you.

答案1

得分: 5

不。例如,使用您的程序,

$ go run -race dz00dz.go
==================
WARNING: DATA RACE
Read at 0x000000595200 by goroutine 7:
  main.add()
     /home/peter/gopath/src/dz00dz.go:11 +0x3d

Previous write at 0x000000595200 by goroutine 6:
  main.add()
      /home/peter/gopath/src/dz00dz.go:11 +0x59

Goroutine 7 (running) created at:
  main.main()
      /home/peter/gopath/src/dz00dz.go:17 +0x76

Goroutine 6 (finished) created at:
  main.main()
      /home/peter/gopath/src/dz00dz.go:17 +0x76
==================
100
Found 1 data race(s)
exit status 66

参考资料:

引入 Go Race Detector

英文:

No. For example, using your program,

$ go run -race dz00dz.go
==================
WARNING: DATA RACE
Read at 0x000000595200 by goroutine 7:
  main.add()
     /home/peter/gopath/src/dz00dz.go:11 +0x3d

Previous write at 0x000000595200 by goroutine 6:
  main.add()
      /home/peter/gopath/src/dz00dz.go:11 +0x59

Goroutine 7 (running) created at:
  main.main()
      /home/peter/gopath/src/dz00dz.go:17 +0x76

Goroutine 6 (finished) created at:
  main.main()
      /home/peter/gopath/src/dz00dz.go:17 +0x76
==================
100
Found 1 data race(s)
exit status 66

References:

Introducing the Go Race Detector

答案2

得分: 3

我在这里得到了相同的结果,但如果我用以下代码替换:

m++

func add(i int) {
	for j := 0; j < 100000; j++ {
		m++
	}
}

在后一种情况下,我使用调度器跟踪确认Go运行时将工作分配给了多个处理器核心,这解释了差异,因为实际上,m没有受到竞态条件的保护。那么为什么在前一种情况下不会发生这种情况呢?可能是因为简单的整数递增对于Go调度器来说太短,无法将goroutine分配给多个线程,所以它们按顺序在同一个线程中执行。而为什么在C中会发生这种情况:因为您手动将计算分布在多个线程上,所以操作系统调度器可能决定在多个处理器核心上执行它们。

英文:

I am getting the same results here, but not if I replace

m++

with

func add(i int) {
	for j := 0; j &lt; 100000; j++ {
		m++
	}
}

In the latter case, I confirmed using scheduler tracing that the Go runtime distributed the work on several processor cores, which explains the discrepancy because, indeed, m is not protected against race conditions. So why does it not happen in the former case ? Probably because a simple integer increment is too short for the Go scheduler to assign the goroutines to several threads, so they execute in sequence, in the same thread. And why does it happen in C : because you manually distribute the calculations on several threads, so the operating system scheduler may decide to execute them on several processor cores.

答案3

得分: 1

你在这里有一个竞态条件,请尝试使用以下命令测试你的代码:

go test -race

这意味着m++不是线程安全的,请尝试像这样修改代码:

var (
    m int
    mu *sync.RWMutex
)

func add(i int){
    mu.Lock()
    m++
    mu.Unlock()
}

此外,我发现这段代码有点混乱:

  1. 移除m = 0
  2. Sleep替换为WaitGroup
  3. 为什么传递了i但没有使用它?
英文:

You have a race condition here, try to test your code with:

go test -race

It means that m++ is not thread-safe, try something like this:

var (
    m int
    mu *sync.RWMutex
)

func add(i int){
    mu.Lock()
    m++
    mu.Unlock()
}

Also I see this code is a bit dirty:

  1. Remove m = 0
  2. Replace Sleep with WaitGroup
  3. Why do you pass i but do not use it?

答案4

得分: 0

不,你不能得到相同的结果。

package main

import (
	"fmt"
	"time"
)

var m int

func add(i int) {
	m++
}

func main() {
	m = 0
	for i := 0; i < 10000; i++ {
		go add(i)
	}
	time.Sleep(time.Millisecond * 1000)
	fmt.Println(m)
}

请注意,这段代码使用了并发的 goroutine 来调用 add 函数,而 add 函数对变量 m 进行自增操作。由于并发的执行方式不确定,因此最终的结果可能会不同。

英文:

No, you cant get the same result.

package main

import (
	&quot;fmt&quot;
	&quot;time&quot;
)

var m int

func add(i int) {
	m++
}

func main() {
	m = 0
	for i := 0; i &lt; 10000; i++ {
		go add(i)
	}
	time.Sleep(time.Millisecond * 1000)
	fmt.Println(m)
}

答案5

得分: 0

Goroutines不会并行运行。Go的工作调度器会有效地管理所有的goroutines,以确保它们都能得到运行。由于goroutines不会并行运行,上述程序的答案始终为100。但是,有一个名为GOMAXPROCS的设置,默认值为1,但如果将其设置为任意大于1的数字N,答案将每次都不同,因为N个goroutines会并行运行。

英文:

Goroutines do not run in parallel. Go work scheduler manages all the goroutines such that all the goroutines are run effectively. Since, goroutines are not running in parallel, the answer would always be 100 for the above program. But, there is one setting GOMAXPROCS which is by default 1, but if you set it any number N > 1, the answer would be different everytime since N goroutines are running in parallel.

huangapple
  • 本文由 发表于 2017年3月26日 22:49:12
  • 转载请务必保留本文链接:https://go.coder-hub.com/43030245.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定