goroutines总是按照“后进先出”的顺序执行。

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

goroutines always execute "last in first out"

问题

为了更好地了解Go语言,我一直在使用goroutines进行实验,并注意到了一些现象,但不确定具体是什么,希望有人能够解释以下行为。

下面的代码表现如预期:

package main

import (
	"fmt"
)

type Test struct {
	me int
}

type Tests []Test

func (test *Test) show() {
	fmt.Println(test.me)
}

func main() {
	var tests Tests
	for i := 0; i < 10; i++ {
		test := Test{
			me: i,
		}
		tests = append(tests, test)
	}

	for _, test := range tests {
		test.show()
	}
}

它按顺序打印出0到9。

现在,当代码改变如下所示时,它总是先返回最后一个元素,无论使用哪些数字:

package main

import (
	"fmt"
	"sync"
)

type Test struct {
	me int
}

type Tests []Test

func (test *Test) show(wg *sync.WaitGroup) {
	fmt.Println(test.me)
	wg.Done()
}

func main() {
	var tests Tests
	for i := 0; i < 10; i++ {
		test := Test{
			me: i,
		}
		tests = append(tests, test)
	}

	var wg sync.WaitGroup
	wg.Add(10)
	for _, test := range tests {
		go func(t Test) {
			t.show(&wg)
		}(test)
	}
	wg.Wait()
}

这将返回:

9
0
1
2
3
4
5
6
7
8

循环的迭代顺序没有改变,所以我猜这与goroutines有关...
基本上,我想要理解为什么会出现这种行为...我知道goroutines的执行顺序可能与它们被创建的顺序不同,但我的问题是为什么它总是以这种方式运行。好像有些非常明显的东西我没有注意到...

英文:

in the interests of learning more about Go, I have been playing with goroutines, and have noticed something - but am not sure what exactly I'm seeing, and hope someone out there might be able to explain the following behaviour.

the following code does exactly what you'd expect:

package main

import (
  &quot;fmt&quot;
)

type Test struct {
  me int
}

type Tests []Test

func (test *Test) show() {
  fmt.Println(test.me)

}

func main() {
  var tests Tests
  for i := 0; i &lt; 10; i++ {
    test := Test{
      me: i,
    }
    tests = append(tests, test)
  }

  for _, test := range tests {
    test.show()
  }

}

and prints 0 - 9, in order.

now, when the code is changed as shown below, it always returns with the last one first - doesn't matter which numbers I use:

package main

import (
	&quot;fmt&quot;
	&quot;sync&quot;
)

type Test struct {
	me int
}

type Tests []Test

func (test *Test) show(wg *sync.WaitGroup) {
	fmt.Println(test.me)
	wg.Done()

}

func main() {
	var tests Tests
	for i := 0; i &lt; 10; i++ {
		test := Test{
			me: i,
		}
		tests = append(tests, test)
	}

	var wg sync.WaitGroup
	wg.Add(10)
	for _, test := range tests {
		go func(t Test) {
			t.show(&amp;wg)
		}(test)
	}
	wg.Wait()

}

this will return:
9
0
1
2
3
4
5
6
7
8

the order of iteration of the loop isn't changing, so I guess that it is something to do with the goroutines...
basically, I am trying to understand why it behaves like this...I understand that goroutines can run in a different order than the order in which they're spawned, but, my question is why this always runs like this. as if there's something really obvious I'm missing...

答案1

得分: 3

预期的输出是伪随机的,

package main

import (
	"fmt"
	"runtime"
	"sync"
)

type Test struct {
	me int
}

type Tests []Test

func (test *Test) show(wg *sync.WaitGroup) {
	fmt.Println(test.me)
	wg.Done()

}

func main() {
	fmt.Println("GOMAXPROCS", runtime.GOMAXPROCS(0))
	var tests Tests
	for i := 0; i < 10; i++ {
		test := Test{
			me: i,
		}
		tests = append(tests, test)
	}

	var wg sync.WaitGroup
	wg.Add(10)
	for _, test := range tests {
		go func(t Test) {
			t.show(&wg)
		}(test)
	}
	wg.Wait()

}

输出:

$ go version
go version devel +af15bee Fri Jan 29 18:29:10 2016 +0000 linux/amd64
$ go run goroutine.go
GOMAXPROCS 4
9
4
5
6
7
8
1
2
3
0
$ go run goroutine.go
GOMAXPROCS 4
9
3
0
1
2
7
4
8
5
6
$ go run goroutine.go
GOMAXPROCS 4
1
9
6
8
4
3
0
5
7
2
$ 

你是在 Go playground 上运行吗?Go playground 是确定性的,这样更容易缓存程序。

或者,你是在设置 runtime.GOMAXPROCS = 1 吗?这会按顺序一次运行一个任务。这就是 Go playground 的做法。

<details>
<summary>英文:</summary>

As expected, the ouput is pseudo-random,

    package main
    
    import (
    	&quot;fmt&quot;
    	&quot;runtime&quot;
    	&quot;sync&quot;
    )
    
    type Test struct {
    	me int
    }
    
    type Tests []Test
    
    func (test *Test) show(wg *sync.WaitGroup) {
    	fmt.Println(test.me)
    	wg.Done()
    
    }
    
    func main() {
    	fmt.Println(&quot;GOMAXPROCS&quot;, runtime.GOMAXPROCS(0))
    	var tests Tests
    	for i := 0; i &lt; 10; i++ {
    		test := Test{
    			me: i,
    		}
    		tests = append(tests, test)
    	}
    
    	var wg sync.WaitGroup
    	wg.Add(10)
    	for _, test := range tests {
    		go func(t Test) {
    			t.show(&amp;wg)
    		}(test)
    	}
    	wg.Wait()
    
    }

Output:

    $ go version
    go version devel +af15bee Fri Jan 29 18:29:10 2016 +0000 linux/amd64
    $ go run goroutine.go
    GOMAXPROCS 4
    9
    4
    5
    6
    7
    8
    1
    2
    3
    0
    $ go run goroutine.go
    GOMAXPROCS 4
    9
    3
    0
    1
    2
    7
    4
    8
    5
    6
    $ go run goroutine.go
    GOMAXPROCS 4
    1
    9
    6
    8
    4
    3
    0
    5
    7
    2
    $ 

Are you running in the Go playground? The Go playground, by design, is deterministic, which makes it easier to cache programs.

Or, are you running with runtime.GOMAXPROCS = 1? This runs one thing at a time, sequentially. This is what the Go playground does.


</details>



# 答案2
**得分**: 2

Go 1.5版本以后,Go协程的调度是随机的。因此,即使顺序看起来是一致的,**不要依赖于它**。

参见[Go 1.5发布说明][1]:

&gt; 在Go 1.5中,协程调度的顺序已经改变。语言从未定义调度器的属性,但是依赖调度顺序的程序可能会因此改变而出错。我们已经看到一些(错误的)程序受到了这个改变的影响。如果你有依赖于调度顺序的程序,你需要对它们进行更新。
&gt; 
&gt; 另一个可能会引起问题的改变是,运行时现在将同时运行的默认线程数(由GOMAXPROCS定义)设置为CPU上可用的核心数。在之前的版本中,默认值为1。不希望在多个核心上运行的程序可能会意外中断。可以通过删除限制或显式设置GOMAXPROCS来进行更新。有关此更改的更详细讨论,请参见设计文档。

  [1]: https://golang.org/doc/go1.5

<details>
<summary>英文:</summary>

Go routines are scheduled randomly since Go 1.5. So, even if the order looks consistent, **don&#39;t rely on it**.

See [Go 1.5 release][1] note :

&gt; In Go 1.5, the order in which goroutines are scheduled has been changed. The properties of the scheduler were never defined by the language, but programs that depend on the scheduling order may be broken by this change. We have seen a few (erroneous) programs affected by this change. If you have programs that implicitly depend on the scheduling order, you will need to update them.
&gt; 
&gt; Another potentially breaking change is that the runtime now sets the default number of threads to run simultaneously, defined by GOMAXPROCS, to the number of cores available on the CPU. In prior releases the default was 1. Programs that do not expect to run with multiple cores may break inadvertently. They can be updated by removing the restriction or by setting GOMAXPROCS explicitly. For a more detailed discussion of this change, see the design document.


  [1]: https://golang.org/doc/go1.5

</details>



huangapple
  • 本文由 发表于 2016年2月2日 20:03:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/35153010.html
匿名

发表评论

匿名网友

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

确定