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

2024-02-10 15:52

本文主要是介绍Golang的for循环变量和goroutine的陷阱,1.22版本的更新,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

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

package mainimport "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 mainimport "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 mainimport "fmt"func main() {done := make(chan bool)values := []string{"chen", "hai", "feng"}for _, v := range values {fmt.Println("start")val := vgo func() {fmt.Println(val)done <- true}()fmt.Println("end")}for _ = range values {<-done}
}

此时的运行结果

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

package mainimport "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 mainimport "fmt"func main() {for i := range 10 {fmt.Println(i)}
}

 

这篇关于Golang的for循环变量和goroutine的陷阱,1.22版本的更新的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/697502

相关文章

golang内存对齐的项目实践

《golang内存对齐的项目实践》本文主要介绍了golang内存对齐的项目实践,内存对齐不仅有助于提高内存访问效率,还确保了与硬件接口的兼容性,是Go语言编程中不可忽视的重要优化手段,下面就来介绍一下... 目录一、结构体中的字段顺序与内存对齐二、内存对齐的原理与规则三、调整结构体字段顺序优化内存对齐四、内

Python中顺序结构和循环结构示例代码

《Python中顺序结构和循环结构示例代码》:本文主要介绍Python中的条件语句和循环语句,条件语句用于根据条件执行不同的代码块,循环语句用于重复执行一段代码,文章还详细说明了range函数的使... 目录一、条件语句(1)条件语句的定义(2)条件语句的语法(a)单分支 if(b)双分支 if-else(

java中不同版本JSONObject区别小结

《java中不同版本JSONObject区别小结》本文主要介绍了java中不同版本JSONObject区别小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们... 目录1. FastjsON2. Jackson3. Gson4. org.json6. 总结在Jav

C#多线程编程中导致死锁的常见陷阱和避免方法

《C#多线程编程中导致死锁的常见陷阱和避免方法》在C#多线程编程中,死锁(Deadlock)是一种常见的、令人头疼的错误,死锁通常发生在多个线程试图获取多个资源的锁时,导致相互等待对方释放资源,最终形... 目录引言1. 什么是死锁?死锁的典型条件:2. 导致死锁的常见原因2.1 锁的顺序问题错误示例:不同

Python判断for循环最后一次的6种方法

《Python判断for循环最后一次的6种方法》在Python中,通常我们不会直接判断for循环是否正在执行最后一次迭代,因为Python的for循环是基于可迭代对象的,它不知道也不关心迭代的内部状态... 目录1.使用enuhttp://www.chinasem.cnmerate()和len()来判断for

Java循环创建对象内存溢出的解决方法

《Java循环创建对象内存溢出的解决方法》在Java中,如果在循环中不当地创建大量对象而不及时释放内存,很容易导致内存溢出(OutOfMemoryError),所以本文给大家介绍了Java循环创建对象... 目录问题1. 解决方案2. 示例代码2.1 原始版本(可能导致内存溢出)2.2 修改后的版本问题在

浅析Rust多线程中如何安全的使用变量

《浅析Rust多线程中如何安全的使用变量》这篇文章主要为大家详细介绍了Rust如何在线程的闭包中安全的使用变量,包括共享变量和修改变量,文中的示例代码讲解详细,有需要的小伙伴可以参考下... 目录1. 向线程传递变量2. 多线程共享变量引用3. 多线程中修改变量4. 总结在Rust语言中,一个既引人入胜又可

golang1.23版本之前 Timer Reset方法无法正确使用

《golang1.23版本之前TimerReset方法无法正确使用》在Go1.23之前,使用`time.Reset`函数时需要先调用`Stop`并明确从timer的channel中抽取出东西,以避... 目录golang1.23 之前 Reset ​到底有什么问题golang1.23 之前到底应该如何正确的

Golang操作DuckDB实战案例分享

《Golang操作DuckDB实战案例分享》DuckDB是一个嵌入式SQL数据库引擎,它与众所周知的SQLite非常相似,但它是为olap风格的工作负载设计的,DuckDB支持各种数据类型和SQL特性... 目录DuckDB的主要优点环境准备初始化表和数据查询单行或多行错误处理和事务完整代码最后总结Duck

Golang的CSP模型简介(最新推荐)

《Golang的CSP模型简介(最新推荐)》Golang采用了CSP(CommunicatingSequentialProcesses,通信顺序进程)并发模型,通过goroutine和channe... 目录前言一、介绍1. 什么是 CSP 模型2. Goroutine3. Channel4. Channe