Golang常见错误之值拷贝和for循环中的单一变量详解

栏目: 编程语言 · Go · 发布时间: 7年前

内容简介:这篇文章主要给大家介绍了关于Golang常见错误之值拷贝和for循环中单一变量的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。

前言

golang(中文名:go语言)是谷歌2009发布的第二款开源编程语言。Go语言专门针对多处理器系统应用程序的编程进行了优化,使用 Go 编译的程序可以媲美C或C++代码的速度,而且更加安全、支持并行进程。。如果你想知道得更多,请移步至官网golang官网

在 Go 中函数的调用是值拷贝 copy value,而且在 for 循环中 v 的变量始终是一个变量。如果 v 是 pointer,print 这个 method 接收的是指针的拷贝,for 循环体中每次迭代 v 的 pointer value 都是不同的,所以输出不同。

在 Go 常见的错误一文中 http://devs.cloudimmunity.com/gotchas-and-common-mistakes-in-go-golang/ 有这么一段代码:

package main

import ( 
 "fmt"
 "time"
)

type field struct { 
 name string
}

func (p *field) print() { 
 fmt.Println(p.name)
}

func main() { 
 data := []field{{"one"},{"two"},{"three"}}

 for _,v := range data {
 go v.print()
 }

 time.Sleep(3 * time.Second)
 //goroutines print: three, three, three
}

把 field slice 的类型改为 pointer 结果又不同:

package main

import ( 
 "fmt"
 "time"
)

type field struct { 
 name string
}

func (p *field) print() { 
 fmt.Println(p.name)
}

func main() { 
 data := []*field{{"one"},{"two"},{"three"}}

 for _,v := range data {
 v := v
 go v.print()
 }

 time.Sleep(3 * time.Second)
 //goroutines print: one, two, three
}

这两段代码的差异究竟是如何导致结果的不同?

我对上面的代码 for 循环中的部分进行了一下改造,改造之后对应的代码分别是:

slice 是非指针

 data := []field{{"one"},{"two"},{"three"}}

 for _,v := range data {
 pp := (*field).print
 go pp(&v) //非 pointer
 }

slice 是指针

 data := []*field{{"one"},{"two"},{"three"}}

 for _,v := range data {
 pp := (*field).print
 go pp(v) // pointer
 }

改造之后再去看原来的代码就能看出最明显的差异在 print 的这个 method 的 receiver 的传递上。

在 Go 中函数的调用是值拷贝 copy value,而且在 for 循环中 v 的变量始终是一个变量。

如果 v 是 pointer,print 这个 method 接收的是指针的拷贝,for 循环体中每次迭代 v 的 pointer value 都是不同的,所以输出不同。

如果 v 是一个普通的 struct,for 循环体中每次迭代 &v 都是 v 这个变量本身的 pointer,也就是总是指向同一个 field,由于在很大程度上这段代码中的 goroutine 都是在 for 结束之后才执行,而此时 v 将会指向最后一个 field,也就是 {"three"},所以输出相同。

有人说 one、two、three 的随机输出是因为 CPU 是多核的原因导致的,如果改成单核就是顺序输出,这样的说法并不是特别准确。理论上来讲 goroutine 的调度是有一定的随机性的,也就是即使是单核输出也有可能是随机的,只是在运行如此简单的例子时一般机器环境都不会导致这 3 个简单的 goroutine 出现交叉执行。比如可以在 print 输出之前模拟 io 繁忙的来达到即使是单核也可能是随机输出的目的。

 if rand.Intn(100) > 20 {
 time.Sleep(1 * time.Second)
 }

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Nine Algorithms That Changed the Future

Nine Algorithms That Changed the Future

John MacCormick / Princeton University Press / 2011-12-27 / GBP 19.95

Every day, we use our computers to perform remarkable feats. A simple web search picks out a handful of relevant needles from the world's biggest haystack: the billions of pages on the World Wide Web.......一起来看看 《Nine Algorithms That Changed the Future》 这本书的介绍吧!

MD5 加密
MD5 加密

MD5 加密工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具