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)
	}
}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/380078.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Elasticsearch(四)

是这样的前面的几篇笔记&#xff0c;感觉对我没有形成知识体系&#xff0c;感觉乱糟糟的&#xff0c;只是大概的了解了一些基础知识&#xff0c;仅此而已&#xff0c;而且对于这技术栈的学习也是为了在后面的java开发使用&#xff0c;但是这里的API学的感觉有点乱&#xff01;然…

JavaScript 入门 完整版

目录 第一个知识点&#xff1a;引入js文件 内部引用: 外部引用: 第二个知识点&#xff1a;javascript的基本语法 定义变量&#xff1a; 条件控制(if - else if - else) 第三个知识点&#xff1a;javascript里的数据类型、运算符&#xff1a; 数字类型 字符串类型 布尔…

前端JavaScript篇之对执行上下文的理解

目录 对执行上下文的理解创建执行上下文 对执行上下文的理解 当我们在执行JavaScript代码时&#xff0c;JavaScript引擎会创建并维护一个执行上下文栈来管理执行上下文。执行上下文有三种类型&#xff1a;全局执行上下文、函数执行上下文和eval函数执行上下文。 在写代码的时…

FRP内网穿透需要注意的事情

安全性 SSH设置好密钥后&#xff0c;一定要关闭密码登陆。现在暴力破解策略往往是先派小鸡端口扫描看看谁可以密码访问&#xff0c;如果可以&#xff0c;然后定点爆破就开始了。不允许root登陆。FRP使用token验证。FRP服务端要输出配置文件&#xff0c;info等级就能显示访问ip…

mac docker 宿主机和容器间网络打通

动因 是这样&#xff0c;笔者最近满怀欣喜入手Docker&#xff0c;看着各种文章命令都是不断点头称道&#xff1a;“嗯嗯&#xff0c;不错不错”,在接下来终于准备大干一场的时候碰壁了&#xff0c;主要情况是说在Mac中跑了第一把的时候发现碰到&#xff0c;虚拟机和宿主机居然…

C语言笔试题之两数相加(多次反转链表实现)

实例要求&#xff1a; 1、给定两个非空链表&#xff08;l1和l2&#xff09;来代表两个非负整数&#xff1b;2、数字最高位位于链表开始位置&#xff1b;3、它们的每个节点只存储一位数字&#xff1b;4、将这两数相加会返回一个新的链表&#xff1b; 案例展示&#xff1a; 实例…

Leetcode刷题笔记题解(C++):面试题 08.07. 无重复字符串的排列组合

思路&#xff1a;因为字符之间互不相同&#xff0c;故使用全排列的方式去解题&#xff1b; 字符串长度为n&#xff0c;将第一个字母分别与后面每一个字母进行交换&#xff0c;生成n种不同的全排列&#xff1b;再用第二个元素与后面每一个元素进行交换&#xff0c;生成n - 1种不…

K8S之运用亲和性设置Pod的调度约束

亲和性 Node节点亲和性硬亲和实践软亲和性实践 Pod节点亲和性和反亲和性pod亲和性硬亲和实践 pod反亲和性 Pod 的yaml文件里 spec 字段中包含一个 affinity 字段&#xff0c;使用一组亲和性调度规则&#xff0c;指定pod的调度约束。 kubectl explain pods.spec.affinity 配置…

c语言实现io多路复用(select),进程,线程并发服务器

io多路复用&#xff08;select&#xff09;代码 #include<myhead.h> #include <sys/select.h> #define PORT 8888 #define IP "192.168.250.100" int main(int argc, char const *argv[]) { //创建套接字int sfd socket(AF_INET, SOCK_STREAM, 0…

Leetcode—60. 排列序列【困难】

2024每日刷题&#xff08;113&#xff09; Leetcode—60. 排列序列 算法思想 实现代码 class Solution { public:string getPermutation(int n, int k) {vector<int> nums(n);// f[i] i!vector<int> f(n 1, 1); string ans;iota(nums.begin(), nums.end(), 1…

Blazor入门100天 : 自做一个支持长按事件的按钮组件

好长时间没继续写这个系列博客了, 不知道大家还记得我吗? 话不多说,直接开撸. 配套源码 demo https://blazor.app1.es/b19LongPressButton ####1. 新建 net8 blazor 工程 b19LongPressButton 至于用什么模式大家各取所需, 我创建的是ssr单工程, 如果大家不小心建立错了按页…

软考 系统分析师系列知识点之信息系统战略规划方法(4)

接前一篇文章&#xff1a;软考 系统分析师系列知识点之信息系统战略规划方法&#xff08;3&#xff09; 所属章节&#xff1a; 第7章. 企业信息化战略与实施 第4节. 信息系统战略规划方法 7.4.2 关键成功因素法 关键成功因素&#xff08;Critical Success Factors&#xff0c…

Vue3.0(五):Vue-Router 4.x详解

Vue-Router详解 vue-router教程 认识前端路由 路由实际上是网络工程中的一个术语 在架构一个网络的时候&#xff0c;常用到两个很重要的设备—路由器和交换机路由器实际上就是分配ip地址&#xff0c;并且维护着ip地址与电脑mac地址的映射关系通过映射关系&#xff0c;路由器…

放假--寒假自学版 day1(补2.5)

fread 函数&#xff1a; 今日练习 C语言面试题5道~ 1. static 有什么用途&#xff1f;&#xff08;请至少说明两种&#xff09; 1) 限制变量的作用域 2) 设置变量的存储域 2. 引用与指针有什么区别&#xff1f; 1) 引用必须被初始化&#xff0c;指针不必。 2) 引用初始…

数据结构与算法-链表(力扣附链接)

之前我们对C语言进行了一定的学习&#xff0c;有了一些基础之后&#xff0c;我们就可以学习一些比较基础的数据结构算法题了。这部分的知识对于我们编程的深入学习非常有用&#xff0c;对于一些基本的算法&#xff0c;我们学习之后&#xff0c;就可以参加一些编程比赛了&#x…

java基础(2) 面向对象编程-java核心类

面向对象 面向对象对应的就是面向过程&#xff0c; 面向过程就是一步一步去操作&#xff0c;你需要知道每一步的步骤。 面向对象的编程以对象为核心&#xff0c;通过定义类描述实体及其行为&#xff0c;并且支持继承、封装和多态等特性 面向对象基础 面向对象编程&#xff0…

吉他学习:识谱,认识节奏,视唱节奏,节拍器的使用

第九课 识谱https://m.lizhiweike.com/lecture2/29362692 第十课 基础乐理(二)——节奏篇https://mp.csdn.net/mp_blog/creation/editor?spm=1011.2124.3001.6192

CPU和GPU有什么区别,玩游戏哪个更重要?

大家好&#xff01;今天我们要聊的话题是CPU和GPU&#xff0c;它们在电脑中扮演着重要的角色&#xff0c;虽然看起来只是两个简单的缩写&#xff0c;但它们的功能和影响是截然不同的&#xff01; 那么&#xff0c;究竟CPU和GPU有什么区别呢&#xff1f;在玩游戏时&#xff0c;…

OCP使用web console创建和构建应用

文章目录 环境登录创建project赋予查看权限部署第一个image检查pod扩展应用 部署一个Python应用连接数据库创建secret加载数据并显示国家公园地图 清理参考 环境 RHEL 9.3Red Hat OpenShift Local 2.32 登录 在 crc start 启动crc时&#xff0c;可以看到&#xff1a; .....…

寒假作业2024.2.6

1.现有无序序列数组为23,24,12,5,33,5347&#xff0c;请使用以下排序实现编程 函数1:请使用冒泡排序实现升序排序 函数2:请使用简单选择排序实现升序排序 函数3:请使用直接插入排序实现升序排序 函数4:请使用插入排序实现升序排序 #include <stdio.h> #include <stdl…