go语言例程:并发中的型参

Posted 懒散的狒狒

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了go语言例程:并发中的型参相关的知识,希望对你有一定的参考价值。

这是一个golang官网上贴出的例程:


for _, v := range []string{"a", "b", "c"} {
    go func() {         fmt.Println(v)     }() }  


它在很多go语言面试中都有被问到。首先它的运行结果是:


c c c


但背后的工作原理面试者阐述起来通常会有遗漏。这个涉及到的go语言知识点包括:

  • 循环变量的对象内存分配;

  • 并发中内存的读写特性;

  • 形参的内存分配;


但是在v被更新的过程中,goroutine并发函数不能获取到v的值,这就涉及到go语言并发内存的读写互斥属性。在并发过程中,内存的读写操作是互斥的,在写操作同时,读操作被阻塞,直到写操作完成之后,读操作才会执行。

因此上述例程中,循环过程中3个goroutine函数中读取v数据的代码一直被阻塞。当循环体结束之后,读取操作才会继续。而此时v的值是"c",因此3个goroutine读取出来的v都是"c"。

如果我们想要得到输出结果:

a b c

需要对代码作出修改,对闭包goroutine函数加上一个形参:

for _, v := range []string{"a", "b", "c"} {
    go func(v string) {         fmt.Println(v)     }(v) }  

这是因为编译器会为每个形参都都分配一个单独的内存空间,在外面实参传递进来的时候,实参数据被复制给形参,而这个动作是在外部循环的进程(或者说协程)上完成,因此不存在读写互斥的问题。

数据被复制给形参之后,3个goroutine读取到单独的内存数据,分别是"a","b","c"。从而输出我们预期的结果。


以上是关于go语言例程:并发中的型参的主要内容,如果未能解决你的问题,请参考以下文章

[Go] 并发和并行的区别

我的Go+语言初体验——零基础学习 Go+ 爬虫

如何正确使用通道来控制并发?

Go语言并发编程

Go语言中的数据竞争模式

GO语言并发编程-原子操作