闭包是一种能够让你用非常舒服的方式来编程的小技巧,Go也支持闭包。

假设从来没有接触过闭包,想在一開始就弄懂什么是闭包(closure)是非常困难的,就像递归一样,直到你真正写过、用过它,你才干真正的对它有一个更详细的认识。

闭包就是一个函数,这个函数包括了执行它所需的上下文环境,这个环境可能是几个变量或者也会是其它的(通常就是变量)。说闭包是一个函数不对,更确切地说。闭包是一个打包了其作用域外部的上下文环境的一段执行环境。假设一时间没有理解这段闭包的含义也不要紧。这是一个循序渐进的过程。

那么我们来看一个网上最通用的解说闭包的样例:

package main

import "fmt"

func A() func() int {
value := 0
return func() int {
value++
return value
} // A()到这里时按理说应该已经销毁掉了局部变量value
} func main() {
B := A()
fmt.Println(B())
fmt.Println(B())
fmt.Println(B())
}

执行结果为:

1
2
3

好好看下这段代码,我们定义一个函数A(),在A中我们又定义了还有一个函数B(),并且B将会作为返回值返回给我们。B的作用就是每次调用都会给A的局部变量value增1。

可是当A函数执行完之后。按理说局部变量value应该已经被销毁了,B无法再对其进行操作,可是从执行结果来看。value却还"活着"。没错,这就是所谓的打包了上下文环境的函数,B就是一个闭包函数,它不仅定义了自己的操作流程,并且附带着它的上文环境A中的value变量。

从这个样例你可能没太看出闭包有什么作用。可是事实上闭包这个东西或多或少会要求编程语言支持匿名函数并且函数在该语言中式第一类型(能够把函数赋值给变量,能够用函数当參数和返回值)。闭包的作用我也不大好说。像JS这种缺陷比較多的语言使用闭包能够带来保护变量的訪问权限、更好的模块化这种优点,而对于Go,我认为闭包最大的优点就是:

1. 不用特意给某个函数取名字了。省事儿~

2. 能够把某个暂时使用的函数定义在近期的地方

3. 和goroutine使用的时候能够直接用go func() {...}() 这种写法。这就不用把每一个要并发的函数都在当前执行环境的外部预先定义一遍了。

讲完了闭包的基本内容。接下来就讲一讲使用闭包时应该注意的问题了。

原则上讲,闭包用起来的最大优点就是方便,可是不要在不论什么情况下都首先想到使用闭包,由于假设你对闭包缺乏了解。那你写出的代码非常可能会有意外的执行效果。来看一下以下的样例:

package main

import (
"fmt"
) func main() {
done := make(chan bool, 3)
for i := 0; i < 3; i++ {
go func() { //这里是有问题的。每一个routine都打包了外层执行环境中的变量i
fmt.Println(i)
done <- true
}()
}
for i := 0; i < 3; i++ {
<-done
}
}

执行结果是:

3
3
3

假设你细致看下代码的话,应该会认为代码看上去没有不论什么问题。可是结果为甚不是1 2 3呢???事实上Go的官方指南也给出了相关的样例让开发人员们注意闭包和goroutine一起使用时要注意的这个问题。

这个样例中事实上第一个循环中的三个goroutine在创建之后都不会立马执行,由于在他们都绑定了外层执行环境中的变量i,由于外层的执行环境随时会更改i的值。所以知道这个循环结束。三个routine都不能開始执行

这时的解决的方法就是把要用到的外层上下文环境作为參数传递给闭包函数。像这样:

package main

import (
"fmt"
) func main() {
done := make(chan bool, 3)
for i := 0; i < 3; i++ {
go func(index int) { // 把外层环境中的i当做參数传进来
fmt.Println(index)
done <- true
}(i) // 把i传进去
}
for i := 0; i < 3; i++ {
<-done
}
}

这种话闭包函数就不须要再绑定外层环境中的那个变量i了。这个问题一定要注意,由于Go里会常常将goroutine和闭包配合使用,假设没有处理好上下文打包的问题,就非常可能引发意外的执行效果。

假设转载,请注明出处:http://blog.csdn.net/gophers

最新文章

  1. XML文件(2)--使用DOM4J示例
  2. Objective - C 值对象 NSNumber和NSValue
  3. Android不同屏幕适配
  4. Maven 其他功能
  5. p::first-line { text-transform: uppercase }
  6. Infragistics UltraGrid的使用
  7. C#学习笔记二: C#类型详解
  8. jvm 调优 工具
  9. QR Code 码
  10. Caffe可视化之VisualDL
  11. SharePoint 入门级介绍
  12. 群晖IP地址更新问题
  13. 3、谈谈 Java NIO
  14. POJ 3243 // HDU 2815(改下输出,加个判断)
  15. JAVA中的array是通过线性表还是链表实现的呢?
  16. oracle第三天笔记
  17. 记一款bug管理系统(bugdone.cn)的开发过程(4) - 新增BugTalk功能
  18. 怎样把代码复制到word中并保持颜色
  19. 【BZOJ 1492】 [NOI2007]货币兑换Cash 斜率优化DP
  20. TestNG+Maven+IDEA环境搭建

热门文章

  1. android 九宫格(16宫格)控件
  2. USACO holstein 超时代码
  3. Xcode6 引入第三方静态库project的方法
  4. 0x28 IDA*
  5. Java RTTI(类型信息)(.class 类对象)
  6. [IOI 1999] 花店橱窗布置
  7. HTML5-1、标签
  8. 2.linux系统命令详解
  9. Java NIO(二)缓冲区
  10. ZBrush软件如何编辑物体