golang并发不是并行


本文摘自php中文网,作者(*-*)浩,侵删。

并发不等于并行

golang的核心开发人员Rob Pike专门提到了这个话题 (推荐学习:go)

虽然我们在for循环中使用了go 创建了一个goroutine,我们想当然会认为,每次循环变量时,golang一定会执行这个goroutine,然后输出当时的变量。

这时,我们就陷入了思维定势。 默认并发等于并行。

诚然,通过go创建的goroutine是会并发的执行其中的函数代码。

但一定会按照我们所设想的那样每次循环时执行吗?

答案是否定的!

Rob Pike专门提到了golang中并发指的是代码结构中的某些函数逻辑上可以同时运行,但物理上未必会同时运行。而并行则指的就是在物理层面也就是使用了不同CPU在执行不同或者相同的任务。

golang的goroutine调度模型决定了,每个goroutine是运行在虚拟CPU中的(也就是我们通过runtime.GOMAXPROCS(1)所设定的虚拟CPU个数)。

虚拟CPU个数未必会和实际CPU个数相吻合。每个goroutine都会被一个特定的P(虚拟CPU)选定维护,而M(物理计算资源)每次回挑选一个有效P,然后执行P中的goroutine。

每个P会将自己所维护的goroutine放到一个G队列中,其中就包括了goroutine堆栈信息,是否可执行信息等等。默认情况下,P的数量与实际物理CPU的数量相等。

因此当我们通过循环来创建goroutine时,每个goroutine会被分配到不同的P队列中。而M的数量又不是唯一的,当M随机挑选P时,也就等同随机挑选了goroutine。

在本题中,我们设定了P=1。所以所有的goroutine会被绑定到同一个P中。 如果我们修改runtime.GOMAXPROCS的值,就会看到另外的顺序。 如果我们输出goroutine id,就可以看到随机挑选的效果:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

func main() {

wg := sync.WaitGroup{}

wg.Add(20)

for i := 0; i < 10; i++ {

go func() {

var buf [64]byte

n := runtime.Stack(buf[:], false)

idField := strings.Fields(strings.TrimPrefix(string(buf[:n]), "goroutine "))[0]

id, err := strconv.Atoi(idField)

if err != nil {

panic(fmt.Sprintf("cannot get goroutine id: %v", err))

}

fmt.Println("go routine 1 i: ", i, id)

wg.Done()

}()

}

for i := 0; i < 10; i++ {

go func(i int) {

var buf [64]byte

n := runtime.Stack(buf[:], false)

idField := strings.Fields(strings.TrimPrefix(string(buf[:n]), "goroutine "))[0]

id, err := strconv.Atoi(idField)

if err != nil {

panic(fmt.Sprintf("cannot get goroutine id: %v", err))

}

fmt.Println("go routine 2 i: ", i, id)

wg.Done()

}(i)

 

}

wg.Wait()

}

输出如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

go routine 2 i: 9 24

go routine 1 i: 10 11

go routine 1 i: 10 5

go routine 1 i: 10 6

go routine 2 i: 3 18

go routine 1 i: 10 9

go routine 1 i: 10 10

go routine 1 i: 10 8

go routine 2 i: 0 15

go routine 2 i: 4 19

go routine 2 i: 6 21

go routine 1 i: 10 7

go routine 1 i: 10 14

go routine 2 i: 7 22

go routine 2 i: 8 23

go routine 1 i: 10 13

go routine 2 i: 5 20

go routine 1 i: 10 12

go routine 2 i: 1 16

go routine 2 i: 2 17

?> ~/S/g/g/s/t/C/goroutine ./goroutine

go routine 1 i: 10 11

go routine 2 i: 9 24

go routine 1 i: 10 6

go routine 1 i: 10 14

go routine 1 i: 10 9

go routine 1 i: 10 10

go routine 1 i: 10 12

go routine 2 i: 0 15

go routine 1 i: 10 13

go routine 1 i: 10 5

go routine 2 i: 1 16

go routine 2 i: 5 20

go routine 1 i: 10 7

go routine 2 i: 7 22

go routine 2 i: 3 18

go routine 2 i: 2 17

go routine 2 i: 4 19

go routine 1 i: 10 8

go routine 2 i: 8 23

go routine 2 i: 6 21

我们再回到这道题中,虽然在循环中通过go定义了一个goroutine。但我们说到了,并发不等于并行。因此虽然定义了,但此刻不见得就会去执行。

需要等待M选择P之后,才能去执行goroutine。 关于golang中goroutine是如何进行调度的(GPM模型),可以参考Scalable Go Scheduler Design Doc或者LearnConcurrency

以上就是golang并发不是并行的详细内容,更多文章请关注木庄网络博客!!

相关阅读 >>

android one和android go有什么区别?

手撸golang 基本数据结构与算法 冒泡排序

awesome go!高玩gopher都会参考的go资源集合

go语言标准库之flag

golang判断文件是否存在的方法

详解go 语言中的方法

手撸golang 架构设计原则 接口隔离原则

使用 go-randgen 测试 join 查询

分享 echo-framework 项目基础框架

新手入门golang开发的注意事项

更多相关阅读请进入《golang》频道 >>




打赏

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码打赏,您说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

分享从这里开始,精彩与您同在

评论

管理员已关闭评论功能...