Golang的for循环变量和goroutine的陷阱,1.22版本的更新

先来看一段golang 1.22版本之前的for循环的代码

复制代码
package main

import "fmt"

func main() {
	done := make(chan bool)

	values := []string{"chen", "hai", "feng"}

	for _, v := range values {
		fmt.Println("start")

		go func() {
			fmt.Println(v)
			done <- true
		}()

		fmt.Println("end")
	}

	for _ = range values {
		<-done
	}
}

运行结果

输出的都是最后的"feng"

在for循环体里,匿名函数和循环变量v形成了闭包。循环变量v只会创建一次,每次迭代都会更新。而且这样的写法会导致for循环结束后才执行goroutine代码,这时候变量v里保存的是最后一个值,所以这里会输出"feng"。

以下提供两种常用的正确写法

第一种方法是在匿名函数中添加参数val,每个val都会被独立计算并保存到goroutine的栈中,所以可以达到预期的结果

复制代码
package main

import "fmt"

func main() {
	done := make(chan bool)

	values := []string{"chen", "hai", "feng"}

	for _, v := range values {
		fmt.Println("start")

		go func(val interface{}) {
			fmt.Println(val)
			done <- true
		}(v)

		fmt.Println("end")
	}

	for _ = range values {
		<-done
	}
}

此时的运行结果

第二种写法:在for循环体内定义新的变量。循环体内定义的变量在遍历的过程中是不共享的,所以可以达到期望的效果。

复制代码
package main

import "fmt"

func main() {
	done := make(chan bool)

	values := []string{"chen", "hai", "feng"}

	for _, v := range values {
		fmt.Println("start")

		val := v
		go func() {
			fmt.Println(val)
			done <- true
		}()

		fmt.Println("end")
	}

	for _ = range values {
		<-done
	}
}

此时的运行结果

升级到最新版本1.22,同样的代码

复制代码
package main

import "fmt"

func main() {
	done := make(chan bool)

	values := []string{"chen", "hai", "feng"}

	for _, v := range values {
		fmt.Println("start")

		go func() {
			fmt.Println(v)
			done <- true
		}()

		fmt.Println("end")
	}

	for _ = range values {
		<-done
	}
}

现在运行结果如下

在golang 1.22中,循环的每次迭代都会创建新的变量,有效避免了以往版本中常见的闭包陷阱,提高了代码的安全性。

另外,1.22之前的版本,for range仅支持array, slice, string, map, channel等类型,现在新增了interger类型,这意味着我们可以像这样写代码

复制代码
package main

import "fmt"

func main() {
	for i := range 10 {
		fmt.Println(i)
	}
}
相关推荐
溪语流沙10 分钟前
【每天一个CSS | Day41】开关三连:一套状态机,三种视觉翻译
前端·css·html5·每日一练·按钮
变与不变80617 分钟前
css定位布局精讲
前端·css
余额瞒着我当琳28 分钟前
Linux初识:Linux的历史,计算机的发展脉络,发行版本,选购云服务器,安装Xshell,第一次连接,基础命令指南
linux·运维·服务器
小杨互联网29 分钟前
Go 现代 CLI 工具离线镜像合集
开发语言·后端·golang·开源·go·命令行
FRing0339 分钟前
JavaScript入门到精通三十三(异常处理、call方法、apply方法、bind方法、防抖、节流)
开发语言·前端·javascript
垆边人似月.1 小时前
RDMA 文件传输踩坑(四):从固定延时改造为 READY/ACK 协议
linux·服务器·网络
服务器硬件项目经理1 小时前
《超节点修仙录》· 第一卷·炼气期【第04话】 超节点 vs 普通服务器——不就是多插了几张卡吗?
运维·服务器·人工智能·嵌入式硬件·深度学习·机器学习
计算机魔术师1 小时前
OpenAI 砍掉 100 亿融资目标,AI 资本市场的温度计变了
前端
jktantan2 小时前
(二)Vue 3 的 attrs 不是响应式:el-form 扩展属性同步的三种方案
前端·javascript·vue.js·element-plus
程序猿老A2 小时前
轻量应用服务器怎么搭建 WordPress站点?
服务器