golang闭包里的坑

Golang闭包的实现原理 闭包的实现 闭包是由函数及其相关引用环境组合而成的实体(即:闭包=函数+引用环境)。 Go中的闭包 闭包是函数式语言中的概念,Go语言是支持闭包的,这只是简单地讲一下在Go语言中闭包是如何实现的。 func f(i int) func() int { return func() int { i++ return i } } 函数f返回... 阅读详情

介绍

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变量的值在作为参数传入匿名函数闭包的时候也就同时被锁定。在使用闭包访问外部变量的时候,问问你自己这个变量时候会发生改变,这样的改变对闭包的运行有什么影响。

 

欢迎加群互相学习,共同进步。QQ群:58099570 | 做人要厚道,转载请注明出处!
关于golang闭包所谓的“ 昨天听了某企业架构师关于golang的经验分享,听到架构师吐槽golang闭包是个大,对此有点不同看法。先贴上一段关于闭包的定义 阅读详情

相关推荐

Pspice安装教程:Windows系统完整指南(含环境变量设置)

详细讲解在Windows系统中完成Pspice安装的全过程,涵盖安装步骤与关键的环境变量设置,帮助用户顺利运行仿真工具。适合初学者快速上手pspice安装教程中的核心环节。

weixin_42230607的博客 1087

golang闭包

闭包 函数类型

songtaiwu的博客 1059

爬虫实战:抖音视频搜索数据爬虫开发实战(附完整Python源码)

【代码】抖音视频搜索数据爬虫开发实战(附完整Python源码)

weixin_61226361的博客 3559

go语言闭包

go语言困惑已久的闭包问题及牛客网做题的理解

weixin_45904051的博客 1106

[go学习笔记.第六章.函数,包,错误处理] 6.闭包,defer,函数参数的传递方式

go语言的闭包,defer,函数参数的传递方式

资深系统架构,深耕高并发研发与 AI 大模型工程化落地。技术栈以 Python (AI/NLP/RAG/Agent) 与 Golang (高并发架构) 为双核心,兼具 PHP 大型应用经验。专注算法落地、复杂系统设计及团队技术赋能 898

Golang闭包陷阱

golang闭包陷阱本质原理其实是range陷阱:Golang for range陷阱 ep 1 func main() { var values = []int{1, 2, 3, 4, 5} for _, value := range values { go func() { fmt.Print(value, " ") }() } time.Sleep(time.Second) } 输出: 5 5 5 5 5 在ep 1中,values其实是一个固定变量,每次循环绑定数组元素的

bob的博客 799

Golang中的闭包详解

闭包是一种在编程语言中常见的概念。它允许一个函数访问其外部作用域中的变量,即使在函数调用完成后,这些变量仍然保持其值。换句话说,闭包是一个函数以及其在创建时捕获的自由变量的组合体。在Golang中,闭包是一种强大而灵活的特性,它能够使我们编写更简洁、可读性更强的代码。接下来,我们将探索Golang闭包的特性、用法以及一些最佳实践。

hitpter的专栏 1656

GoLang - Go闭包用法

当我们不希望给函数起名字的时候,可以使用匿名函数,例如:func(x, y int) int { return x + y }。 这样的一个函数不能够独立存在(编译器会返回错误:non-declaration statement outside function body),但可以被赋值于某个变量,即保存函数的地址到变量中:fplus := func(x, y int) int { return x + y },然后通过变量名对函数进行调用:fplus(3,4)。 当然,您也可以直接对匿名函数进行调用:fu

xiabiao1974的博客 2万+

Go最全Golang中的闭包详解_golang闭包,保洁阿姨看完都会了

闭包是一种在编程语言中常见的概念。它允许一个函数访问其外部作用域中的变量,即使在函数调用完成后,这些变量仍然保持其值。换句话说,闭包是一个函数以及其在创建时捕获的自由变量的组合体。在Golang中,闭包是一种强大而灵活的特性,它能够使我们编写更简洁、可读性更强的代码。接下来,我们将探索Golang闭包的特性、用法以及一些最佳实践。

2401_84900243的博客 1221

聊聊Go面的闭包

聊聊Go面的闭包......

秦怀杂货店 4280

Golang中的闭包详解_golang闭包

只是将部分目录大纲截图出来,每个节点面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新**

2401_84247849的博客 1122

[go]-golang闭包简介与变量引用

文章目录函数匿名函数闭包引用变量记忆效应循环中闭包捕获外部变量的 函数在golang中是一等公民,闭包可以看成函数的高阶应用,是golang高级开发的必备技能。 函数 函数是一等公民(First-class value),即函数可以作为另一个函数的返回值或参数,还可以作为一个变量的值。 匿名函数 匿名函数跟普通函数是一样的,只是他没有名字。 直接使用的匿名函数: sum := func(first int, second int) int { return first + second }(5, 10)

飞鹰技术 1981

Golang 中关于闭包

所谓闭包是指内层函数引用了外层函数中的变量或称为引用了自由变量的函数,其返回值也是一个函数,了解过的语言中有闭包概念的像 js,python,golang 都类似这样。 python 中的闭包可以嵌套函数,像下面这样: def make_adder(addend): def adder(augend): return augend + addend ...

weixin_30735745的博客 136

golang 闭包

golang 闭包 文章目录golang 闭包实例引入什么是函数闭包闭包的概念闭包的价值闭包的机制 实例引入 先来看一段代码,下面的addr函数的返回值是另一个函数,被返回的这个函数中,又对addr函数中的变量进行了累加,然后返回。 在main函数中,使用变量pos接收addr()函数返回的函数对象,然后在for循环中调用5次。 package main import "fmt" func addr() func(int) int { sum := 0 return func(x int) in

qq_49723651的博客 2584

go从入门到精通】闭包和陷阱

高科,先后在上一篇文章当时写完,总是感觉意犹未尽,从匿名函数我联想到了另外一个用法————闭包

yolo模型训练找我,AI游戏合作找我 8万+

上市公司-研发投入与专利数据(2000-2024年)

自2000年起至2024年期间,上市公司在科技研发和创新领域所投入的资本与其所取得的专利产出,构成了庞大的数据集合。该数据集合详细记录了各个上市公司在不同时间段内的研发投入金额、研发人员数量、专利申请数量以及授权专利数量等关键信息。

上一篇: 理解Defer、Panic和Recover
下一篇: Block 的内存管理
future_challenger
博客等级 码龄12年 45粉丝 92原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值