golang闭包里的坑
介绍
go的闭包是一个很有用的东西。但是如果你不了解闭包是如何工作的,那么他也会给你带来一堆的bug。这里我会拿出Go In Action这本书的一部分代码,来说一说在使用闭包的时候可能遇到的坑。全部的代码在github上。
闭包的坑
首先看一段代码:
search/search.go 29 // Launch a goroutine for each feed to find the results.
30 for _, feed := range feeds {
31 // Retrieve a matcher for the search.
32 matcher, exists := matchers[feed.Type]
33 if !exists {
34 matcher = matchers["default"]
35 }
36
37 // Launch the goroutine to perform the search.
38 go func(matcher Matcher, feed *Feed) {
39 Match(matcher, feed, searchTerm, results)
40 waitGroup.Done()
41 }(matcher, feed)
42 }
这段代码从30行开始遍历一个Feed的slice。在for range语句中声明的feed变量的值在每一个循环中都不同。之后从32行的代码在检查一个某个特定的key值是否有值,如果不存在则赋一个默认值。和feed变量一样,matcher的值也是每个循环都不一样。
现在我们可以跳到38行到41行。这几行代码显然还是在for range循环中的。这里我们定义了一个匿名函数,并把这个函数做为一个goroutine运行。这个匿名函数接受两个参数,第一个是Matcher类型的值,第二个是一个Feed类型的指针。在地41行,我们可以蛋刀matcher和feed两个变量被传入了匿名函数中。
这个匿名函数在第39行的实现很有意思。这里我们可以看到一个对Match方法的调用。这个方法接受4个参数,如果你仔细看的话,前两个参数就是我们定义匿名函数声明的而两个参数。后面的两个我们没有在匿名函数中声明。而是作为变量直接在匿名函数使用了。
search/search.go 37 // Launch the goroutine to perform the search.
38 go func(matcher Matcher, feed *Feed) {
39 Match(matcher, feed, searchTerm, results)
40 waitGroup.Done()
41 }(matcher, feed)
42 }
变量searchTerm和results是定义在闭包外部的。我们可以在匿名函数内部直接使用,而不必作为参数传入后再使用。这里就会有一个问题:我们为什么要把变量matcher和feed作为参数传入而其他的两个不是呢?
我在一开始就指出,matcher和feed两个变量的值是如何在每一个for range循环中改变的。searchTerm和results的值不会随着循环而改变,他们的值在每一个goroutine的生命周期中都是常量。当然,这个goroutine就是使用的匿名函数。那么,为什么要这么做呢?
当我们在匿名函数闭包中使用一个变量的时候,我们不必在匿名函数声明的时候作为参数传递。这个匿名函数闭包可以直接访问到定义在其外部的变量,也就是说对这个变量的修改会在匿名函数闭包内部体现出来,也就是这里的goroutine。如果我们把matcher和feed变量这样使用,而不是把他们作为参数传入匿名函数闭包。那么多数情况下gotoutine只会处理for range循环的最后一个值。
在这个例子中,所有的goroutine都会并发执行。for range循环也许在第一个最多第二个goroutine还在运行的时候就运行完了,matcher和feed变量只会有最后一次循环时候的值。也就是说即使不是全部的goroutine也是大部分的goroutine会处理这些变量的相同的值。这种情况适用于searchTerm和results变量,因为他们不会在循环中改变值。
结论
幸好我们可以声明可以接收参数的匿名函数,这些类型的闭包问题也就引刃而解。在我们上面的例子中,当每一个匿名函数都声明在for range的作用域内的时候,matcher和feed变量的值在作为参数传入匿名函数闭包的时候也就同时被锁定。在使用闭包访问外部变量的时候,问问你自己这个变量时候会发生改变,这样的改变对闭包的运行有什么影响。
golang闭包里的坑的更多相关文章
- Golang开发者常见的坑
Golang开发者常见的坑 目录 [−] 初级 开大括号不能放在单独的一行 未使用的变量 未使用的Imports 简式的变量声明仅可以在函数内部使用 使用简式声明重复声明变量 偶然的变量隐藏Accid ...
- golang 闭包
说起golang闭包,在官方手册里面看过一次,没怎么用过,还是因为6哥经常用,阅读他的代码好多闭包,emmm,今天就学习一下. 在过去近十年时间里,面向对象编程大行其道,以至于在大学的教育里,老师也只 ...
- Spring里的坑
Spring里的坑 1. 配置文件配置controller,在Controller中使用注解配置的bean,会出现获取不到bean的情况 步骤一:使用在配置文件 springmvc.xml 中配置的方 ...
- Golang闭包的坑
team leader 发现一个Golang程序的bug,是由不正确使用闭包引起.记载一下,以作备忘. 猜猜一下程序的结果: import ( "fmt" "time&q ...
- golang的timer一些坑
本文代码部分基于dive-to-gosync-workshop的代码 Golang 的NewTimer方法调用后,生成的timer会放入最小堆,一个后台goroutine会扫描这个堆,将到时的time ...
- 【GoLang】golang 闭包 closure 参数传递的蹊跷!
结论: 闭包函数可以直接引用外层代码定义的变量, 但是,注意,闭包函数里面引用的是变量的地址, 当goroutine被调度时,改地址的值才会被传递给goroutine 函数. 介绍 go的闭包是一个很 ...
- go的变量redeclare的问题,golang的一个小坑
go的变量声明有几种方式: 1 通过关键字 var 进行声明 例如:var i int 然后进行赋值操作 i = 5 2 最简单的,通过符号 := 进行声明和赋值 例如: i:=5 golang会 ...
- Golang闭包入门了解
概念闭包就是一个函数与其相关的引用环境组成的一个整体.闭包本质其实是一个函数,但是这个函数会用到函数外的变量,它们共同组成的整体我们叫做闭包. 简单举例说明 package main import ( ...
- List的remove方法里的坑
今天遇到一件怪事,用一个ArrayList添加了一个对象,再调用ArrayList的remove方法删除该对象,当然这时对象是数据库里查出来的,但内容绝对是一样,却发现remove失败了.演示一下,这 ...
随机推荐
- visual c++ 中的stdafx.h头文件的作用
stdafx.h VC工程里面经常见到stdafx.h这个头文件,以前也没有特别注意,但是这个文件用不好经常会出错,所以就GOOGLE了一下,总算是弄清楚了... stdafx的英文全称为:Stand ...
- hibernate最佳实践
1.数据量巨大,性能要求高,hibernate由于在ORM映射中对系统资源消耗也比较高,所以不适合 2.hibernate适合:逻辑复杂,数据量不大. 3.sessionFactory的创建非常消耗资 ...
- str和repr的区别(转)
Python打印值的时候会保持该值在python代码中的状态,不是用户所希望看到的状态.而使用print打印值则不一样,print打印出来的值是用户所希望看到的状态. 例如: >>> ...
- ora-01652无法通过128(在temp表空间中)扩展temp段
有两种错误:1.数据表空间不足 2.临时表空间不足 有两种原因:一是临时表空间空间太小,二是不能自动扩展. 分析过程: 既然是temp表空间有问题,那当然就要从temp表空间说起啦.首先要说明的 ...
- 华为手机不打印log解决方法
在拨号界面输入:*#*#2846579#*#* 进入后台设置---log设置---开启AP日志...开关即可
- strcpy函数;memcpy函数;memmove函数
strcpy函数实现: char* strcpy(char* des,const char* source) { char* r=des; assert((des != NULL) && ...
- iOS codeview
1.环境配置 oclint:http://oclint.org/ xcpretty:https://github.com/supermarin/xcpretty 使用Mac安装xcpretty过程可能 ...
- 使用Spring框架来管理模板类
1. 刚才编写的代码使用的是new的方式,应该把这些类交给Spring框架来管理. 2. 修改的步骤如下 applicationContext.xml中<beans>标签的开头配置为: * ...
- PAT 1070 结绳(25)(代码)
1070 结绳(25 分) 给定一段一段的绳子,你需要把它们串成一条绳.每次串连的时候,是把两段绳子对折,再如下图所示套接在一起.这样得到的绳子又被当成是另一段绳子,可以再次对折去跟另一段绳子串连.每 ...
- Codeforces 612B. Wet Shark and Bishops 模拟
B. Wet Shark and Bishops time limit per test: 2 seconds memory limit per test: 256 megabytes input: ...