team leader 发现一个Golang程序的bug,是由不正确使用闭包引起。记载一下,以作备忘。

猜猜一下程序的结果:

   package main
import (
"fmt"
"time"
) func main() {
arr := []int{, , , , , }
for _, num := range arr {
go func() {
fmt.Println(num) }()
} time.Sleep( * time.Second) }
~

闭包导致的结果如下:

dill@ubuntu-vm:~/GoProjcet/src/exercise$ go run closure.go 

避免闭包:

   package main

   import (
"fmt"
"time"
) func main() {
arr := []int{, , , , , }
for _, num := range arr {
go func(n int) {
fmt.Println(n) }(num)
} time.Sleep( * time.Second) }

得到想要的结果:

dill@ubuntu-vm:~/GoProjcet/src/exercise$ go run closure.go 

说到原因,还要从匿名函数说起。

什么是匿名函数?

命名函数只能在包水平声明,但是我们可以在任意表达式中使用函数字面量的形式给一个函数赋值(相当于在函数中声明函数,不过该函数没有名字,作用域只在表达式内)。函数字面量很像函数声明的,但是在func关键字后没有跟谁函数名字。它是一个表达式,他的值被称为匿名函数。函数字面量使我们可以在用到的地方定义一个函数(随用随定义)。重点是,这种方式定义的函数,可以进入整个lexical 环境,以至于内部函数可以引用外围函数的变量。

本例中,变量单一num被所有的匿名函数共享,即go生成的所有协程均可访问到num,不幸的是num被随后的循环迭代更新。等新goroutinues开始执行时,循环已经更新num,开始下一轮迭代,甚至已经轮询完成。所以当这些goroutine读取num的值时,惊奇的发现,num已经变成了slice中的最后一个值。通过添加一个明确的参数,我们保证我们使用的值是当前go程序正在执行的值。

Golang闭包的坑的更多相关文章

  1. golang闭包里的坑

    介绍 go的闭包是一个很有用的东西.但是如果你不了解闭包是如何工作的,那么他也会给你带来一堆的bug.这里我会拿出Go In Action这本书的一部分代码,来说一说在使用闭包的时候可能遇到的坑.全部 ...

  2. 【GoLang】golang 闭包 closure 参数传递的蹊跷!

    结论: 闭包函数可以直接引用外层代码定义的变量, 但是,注意,闭包函数里面引用的是变量的地址, 当goroutine被调度时,改地址的值才会被传递给goroutine 函数. 介绍 go的闭包是一个很 ...

  3. Golang中的坑二

    Golang中的坑二 for ...range 最近两周用Golang做项目,编写web服务,两周时间写了大概五千行代码(业务代码加单元测试用例代码).用Go的感觉很爽,编码效率高,运行效率也不错,用 ...

  4. Golang 中的坑 一

    Golang 中的坑 短变量声明  Short variable declarations 考虑如下代码: package main import ( "errors" " ...

  5. Golang的防坑小技巧

    Golang的防坑小技巧 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 作为一名小白,在之前没有接触到编程的小伙伴,难免会踩到一些坑,比如说刚刚入门的时候你需要安装环境,学习Gol ...

  6. golang 闭包

    说起golang闭包,在官方手册里面看过一次,没怎么用过,还是因为6哥经常用,阅读他的代码好多闭包,emmm,今天就学习一下. 在过去近十年时间里,面向对象编程大行其道,以至于在大学的教育里,老师也只 ...

  7. 初学者学习golang可能遇到的坑

    我也是个golang初学者,刚入门的话,有些"坑"还是不好发现的.如map只是定义了然后就拿来使用,变量的值覆盖等. 本来打算写一篇的,后面发现有人写的挺不错的,我就把里面的有些坑 ...

  8. golang的哪些坑爷事: package实践

    在golang中package是个困惑的概念, 特别是package还可以与folder不同名, 委实让我恶心了一把. 关于golang的package的最佳实践: package is folder ...

  9. Golang闭包案例分析与普通函数对比

    闭包案例 package main import ( "fmt" "strings" //记住一定引入strings包 ) //①编写一个函数makeSuffi ...

随机推荐

  1. mac 命令行终端 设置代理

    环境: macOS Mojave 10.14.3 iTrem 2 3.2.8 酸酸乳1.1.4.4-R8 查看自己命令行的状态 curl ip.gs 正式开始 一.首先检查自己的酸酸乳是否正常,并在高 ...

  2. mkswap - 建立一个linux交换区

    总览 mkswap [-c] [-vN] [-f] device [size] 描述 mkswap 在一个设备上或者在一个文件里创建一个linux交换区. (该交换区创建后,必须使用 swapon 命 ...

  3. Beta冲刺-(1/3)

    这个作业属于哪个课程 https://edu.cnblogs.com/campus/xnsy/SoftwareEngineeringClass1/ 这个作业要求在哪里 https://edu.cnbl ...

  4. tflite

    1.编译libtensorflow-lite.a库: ubuntu下交叉环境编译: https://blog.csdn.net/computerme/article/details/80345065 ...

  5. HDU 5988 Coding Contest 最小费用流 cost->double

    Problem Description A coding contest will be held in this university, in a huge playground. The whol ...

  6. [易学易懂系列|golang语言|零基础|快速入门|(三)]

    接下来,我们主要讲讲package. 先列举下go的package的一些核心特性: 1.go的package不局限于一个文件,组成一个package的多个文件,编译后实际上和一个文件类似,组成包的不同 ...

  7. C\C++下获取系统进程或线程ID(转)

    在程序开发时有时需要获取线程和进程ID以分析程序运行 ()windows下获取进程或线程ID 通过调用系统提供的GetCurProcessId或GetNowThreadID来获取当前程序代码运行时的进 ...

  8. 2.k8s资源清单

    一.常见资源对象 常见的资源对象:(包括但不仅限于) l  Workload: Pod,ReplicaSet,Deployment,StatefulSet,DaemonSet,Job,Cronjob ...

  9. Python中的网络扫描大杀器Scapy初探

    Python中的网络扫描大杀器Scapy初探     最近经历了Twisted的打击,这个网络编程实在看不懂,都摸不透它的内在逻辑,看来网络编程不是那么好弄的.还好,看到了scapy,这种网络的大杀器 ...

  10. git:Git fetch和git pull的区别, 解决Git报错:error: You have not concluded your merge (MERGE_HEAD exists).

    Git fetch和git pull的区别, 解决Git报错:error: You have not concluded your merge (MERGE_HEAD exists). 解决办法一:保 ...