当前位置: 首页 > news >正文

Golang闭包问题及并发闭包问题

目录

  • Golang闭包问题及并发闭包问题
    • 匿名函数
    • 闭包
      • 闭包可以不传入外部参数,仍然可以访问外部变量
      • 闭包提供数据隔离
    • 并发闭包
      • 为什么
      • 解决方法

Golang闭包问题及并发闭包问题

参考原文链接:https://blog.csdn.net/qq_35976351/article/details/81986496

  • ​ https://www.calhoun.io/what-is-a-closure/
    • ​ https://blog.cloudflare.com/a-go-gotcha-when-closures-and-goroutines-collide/

匿名函数

在引入闭包之前,我们需要先认识匿名函数。匿名函数与普通函数相同,但它没有名称 , 因此称为“匿名函数”。相反,匿名函数是动态创建的,就像变量一样。

我们可以创建一个具有函数类型的变量,然后就可以创建匿名函数并将其分配给变量。

	// 声明函数类型变量var fun func() // 将匿名函数赋值给函数类型变量fun = func() {fmt.Println("匿名函数")	}// 调用函数fun()

匿名函数可以接受参数,返回数据,并执行普通函数可以执行的几乎任何其他操作.

闭包

闭包是一种特殊类型的匿名函数,它引用在函数本身之外声明的变量是匿名函数与匿名函数所引用环境的组合

不仅仅是存储了一个函数的返回值,它同时存储了一个闭包的状态。

闭包可以不传入外部参数,仍然可以访问外部变量

这与常规函数引用全局变量的方式非常相似。你可能不会将这些变量作为参数直接传递到函数中,但函数在调用时可以访问它们。

package mainimport "fmt"func main() {n := 0add := func() int {n += 1return n}fmt.Println(add()) // 1fmt.Println(add()) // 2
}

注意:匿名函数可以访问变量 n,但在调用时从未将其作为参数传入。这就是使它成为关闭的原因!

闭包提供数据隔离

package mainimport "fmt"func main() {counter := newCounter()fmt.Println(counter()) // 1fmt.Println(counter()) // 2// fmt.Println(n)    报错  函数外无法访问闭包变量n ,只有闭包函数才可以持续访问修改变量n
}
// 闭包作为函数返回值
func newCounter() func() int {n := 0return func() int {n += 1return n}
}

在这个例子中,闭包引用变量,即使在函数完成运行之后也是如此。这意味着我们的闭包可以访问一个变量,该变量跟踪它被调用了多少次,但函数之外的其他代码无法访问该变量这是闭包的众多好处之一 - 我们可以在函数调用之间持久化数据,同时将数据与其他代码隔离。

并发闭包

package mainimport "fmt"func main() {for i := 0; i < 10; i++ {fmt.Printf("%d ", i)}
}
//结果很容易看到是: 0 1 2 3 4 5 6 7 8 9

但如果,我们引入groutines并发运行,结果可能会出乎你的意料

	让代码并发执行,最大效率地利用 CPU
格式:runtime.GOMAXPROCS(逻辑CPU数量)这里的逻辑CPU数量可以有如下几种数值:<1:不修改任何数值。=1:单核心执行。>1:多核并发执行。
一般情况下,可以使用 runtime.NumCPU() 查询 CPU 数量,并使用 runtime.GOMAXPROCS() 函数进行设置,例如:
runtime.GOMAXPROCS(runtime.NumCPU())	
package mainimport ("fmt""runtime""sync"
)func main() {// 让代码并发执行,最大效率地利用 CPUruntime.GOMAXPROCS(runtime.NumCPU())var wg sync.WaitGroupfor i := 0; i < 10; i++ {wg.Add(1)go func() {fmt.Printf("%d ", i)wg.Done()}()}wg.Wait()
}

如果你同时思考,那么你可能会预测输出将是数字 0 到 9 以某种随机顺序,具体取决于 10 个 goroutines 的精确运行时间。

但输出实际上是:

10 10 10 10 10 10 10 10 10 10

为什么

为什么?

因为每个 goroutines 的匿名函数 都在用每个 goroutines 生成的十个闭包之间共享单个变量 i

goroutines 的输出将取决于它们何时开始运行的值。在上面的示例中,直到循环终止并具有值 10 之前,它们才真正开始运行

这种现象的原因在于闭包共享外部的变量i,注意到,每次调用go就会启动一个goroutine,这需要一定时间;但是,启动的goroutine与for循环变量递增的groutine不是在同一个goroutine,可以把i认为处于主goroutine中。启动一个goroutine的速度远大于循环执行的速度,所以即使是第一个goroutine刚起启动时,外层的循环也执行到了最后一步了。由于所有的goroutine共享i,而且这个i会在最后一个使用它的goroutine结束后被销毁,所以最后的输出结果都是最后一步的i==10。

总的来说就是:

外层for循环执行,遇到内层go,就启动协程,然后循环+1,但是启动内层协程速度要慢于多个外层循环+1。

可能等到最后一个循环+1,第一个内层go协程才开始运行,加上闭包影响,每个协程并发执行,但是访问的i都是同一个i,都是10.

在外层循环中增加延时效果进行验证

func main() {runtime.GOMAXPROCS(runtime.NumCPU())var wg sync.WaitGroupfor i := 0; i < 10; i++ {wg.Add(1)go func() {fmt.Println(i)wg.Done()}()time.Sleep(1 * time.Second)   // 每次外层for循环+1就时间延时1秒;// 每一步循环至少间隔一秒,而这一秒的时间足够启动一个goroutine了// 这样我们就可以输出正确结果了}wg.Wait()
}

解决方法

在实际的工程中,不可能进行延时,这样就没有并发的优势,一般采取下面两种方法:

  1. 共享的环境变量作为函数参数传递:

    func main() {runtime.GOMAXPROCS(runtime.NumCPU())var wg sync.WaitGroupfor i := 0; i < 5; i++ {wg.Add(1)go func(i int) {fmt.Println(i)wg.Done()}(i)}wg.Wait()
    }
    /*
    输出:
    4
    0
    3
    1
    2
    */

    输出结果不一定按照顺序,这取决于每个goroutine的实际情况,但是最后的结果是不变的。可以理解为,函数参数的传递是瞬时的,而且是在一个goroutine执行之前就完成,所以此时执行的闭包存储了当前i的状态。

    2.使用同名的变量保留当前的状态

func main() {runtime.GOMAXPROCS(runtime.NumCPU())var wg sync.WaitGroupfor i := 0; i < 5; i++ {wg.Add(1)i := i       // 注意这里的同名变量覆盖go func() {fmt.Println(i)wg.Done()}()}wg.Wait()
}
/*
输出结果:
4
2
0
3
1
*/

同名的变量i作为内部的局部变量,覆盖了原来循环中的i,此时闭包中的变量不再是共享外循环的i,而是都有各自的内部同名变量i,赋值过程发生于循环过程中,因此保证了独立。

相关文章:

Golang闭包问题及并发闭包问题

目录Golang闭包问题及并发闭包问题匿名函数闭包闭包可以不传入外部参数&#xff0c;仍然可以访问外部变量闭包提供数据隔离并发闭包为什么解决方法Golang闭包问题及并发闭包问题 参考原文链接&#xff1a;https://blog.csdn.net/qq_35976351/article/details/81986496 ​ htt…...

基频的后处理

基频归一化 基频为什么要归一化&#xff1f;为了消除人际随机差异&#xff0c;提取恒定参数&#xff0c;在语际变异中找到共性。 引言 声调的主要载体就是基频。但是对声调的感知会因人而异&#xff0c;例如某个听感上的高升调&#xff0c;不同的调查人员可能会分别描写成 […...

vue3 toRefs详解

简介 toRefs函数的作用是将响应式对象中的所有属性转换为单独的响应式数据&#xff0c;对象成为普通对象&#xff0c;并且值是关联的。在这个过程中toRefs会做以下两件事&#xff1a; 把一个响应式对象转换成普通对象对该普通对象的每个属性都做一次ref操作&#xff0c;这样每…...

Spring——AOP是什么?如何使用?

一、什么是AOP&#xff1f;在不修改源代码的情况下 增加功能二、底层是什么&#xff1f;动态代理aop是IOC的一个扩展功能&#xff0c;现有IOC&#xff0c;再有AOP&#xff0c;只是在IOC的整个流程中新增的一个扩展点而已&#xff1a;BeanPostProcessorbean的创建过程中有一个步…...

【微服务】认识微服务

目录 1.1 单体、分布式、集群 单体 分布式 集群 1.2 系统架构演变 1.2.1 单体应⽤架构 1.2.2 垂直应⽤架构 1.2.3 分布式架构 1.2.4 SOA架构 1.2.5 微服务架构 1.3 微服务架构介绍 微服务架构的常⻅问题 1.4 SpringCloud介绍 1.4.1 SpringBoot和SpringCloud有啥关…...

【独家】华为OD机试 C 语言解题 - 最长连续子串

最近更新的博客 华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南)华为od机试,独家整理 已参加机试人员的实战技巧文章目录 最近更新的博客使用说明本期…...

【Linux】CentOS7操作系统安装nginx实战(多种方法,超详细)

文章目录前言一. 实验环境二. 使用yum安装nginx2.1 添加yum源2.1.1 使用官网提供的源地址&#xff08;方法一&#xff09;2.1.2 使用epel的方式进行安装&#xff08;方法二&#xff09;2.2 开始安装nginx2.3 启动并进行测试2.4 其他的一些用法&#xff1a;三. 编译方式安装ngin…...

【FMCW 01】中频IF信号

FMCW信号 调频连续波(frequency modulated continuous wave&#xff0c;FMCW)顾名思义&#xff0c;就是对信号的频率进行线性调制的信号。 从时域上看&#xff0c;对频率的调制&#xff0c;就像一把连续的锯齿波。其中每一个锯齿叫做一个chirp&#xff0c;其持续的时间叫做ch…...

【蓝桥杯试题】暴力枚举题型

&#x1f483;&#x1f3fc; 本人简介&#xff1a;男 &#x1f476;&#x1f3fc; 年龄&#xff1a;18 &#x1f91e; 作者&#xff1a;那就叫我亮亮叭 &#x1f4d5; 专栏&#xff1a;蓝桥杯试题 文章目录1. 统计方形&#xff08;数据加强版&#xff09;1. 1 题目描述1.2 思路…...

I.MX6ULL_Linux_系统篇(22) kernel移植

原厂 Linux 内核编译 NXP 提供的 Linux 源码肯定是可以在自己的 I.MX6ULL EVK 开发板上运行下去的&#xff0c;所以我们肯定是以 I.MX6ULL EVK 开发板为参考&#xff0c;然后将 Linux 内核移植到 I.MX6U-ALPHA 开发板上的。 配置编译 Linux 内核 和uboot一样&#xff0c;在编…...

UE实现相机聚焦物体功能

文章目录 1.实现目标2.实现过程2.1 实现原理2.2 源码浅析2.3 具体代码2.3.1 蓝图实现2.3.2 C++实现3.参考资料1.实现目标 实现根据输入的Actor,自动计算出其缩放显示到当前屏幕上相机的最终位置,然后相机飞行过去,实现相机对物体的聚集效果,避免每次输入FlyTo坐标参数,GI…...

算法系列之数值积分的目的

PLC算法里的数字积分器详细介绍请参看下面的文章链接: PLC算法系列之数值积分器(Integrator)_RXXW_Dor的博客-CSDN博客数值积分和微分在工程上的重要意义不用多说,闭环控制的PID控制器就是积分和微分信号的应用。流量累加也会用到。有关积分运算在流量累加上的应用,请参看下…...

【2.4 golang中循环语句for】

1. 循环语句for 1.1.1. Golang for支持三种循环方式&#xff0c;包括类似 while 的语法。 for循环是一个循环控制结构&#xff0c;可以执行指定次数的循环。 语法 Go语言的For循环有3中形式&#xff0c;只有其中的一种使用分号。 for init; condition; post { }for conditi…...

代码随想录 动态规划||343 96

Day35343. 整数拆分力扣题目链接给定一个正整数 n&#xff0c;将其拆分为至少两个正整数的和&#xff0c;并使这些整数的乘积最大化。 返回你可以获得的最大乘积。思路动规逻辑确定dp数组&#xff08;dp table&#xff09;以及下标的含义dp[i]指的是拆分数字i能得到的最大成绩d…...

Python---正则表达式

专栏&#xff1a;python 个人主页&#xff1a;HaiFan. 专栏简介&#xff1a;Python在学&#xff0c;希望能够得到各位的支持&#xff01;&#xff01;&#xff01; 正则表达式前言概念作用和特点使用场景正则符号re模块re.compile()match()search()span()findall()group()sub()…...

Unity入门精要02---纹理

纹理和材质不可分割 本节知识结构 实践&#xff1a;简单贴一张纹理到模型上 首先在属性处添加相关属性 Properties {_Color ("Color Tint", Color) (1, 1, 1, 1)_MainTex ("Main Tex", 2D) "white" {}//加入纹理_Specular ("Specular&q…...

【Day1】一小时入门 python 基础,从安装到入门

文章目录python安装安装python安装 pycharmpython基础输出注释变量输入类型转换运算符自增字符串相关操作比较运算符逻辑运算符条件控制while循环list 列表for 循环range函数元组python 安装 安装python 官网进行下载&#xff1a;官网下载地址这里下载的一直是最新版本的 点…...

2D图像处理:相机标定

文章目录 效果一、相机标定的是什么?二、四个坐标系2.1 世界坐标系(X,Y,Z)2.2 相机坐标系(x,y,x)2.3 图像坐标系2.4 像素坐标系三、坐标系间的变换关系3.1 世界坐标系-->相机坐标系3.2 相机坐标系-->图像坐标系3.3图像坐标系-->像素坐标系四、相机畸变模型4.1 径向…...

windows 下 python 和repo 下载安装环境变量配置

repo 安装成功&#xff0c;但是下载代码 repo init的时候出错 不知道是不是repo windows版本有问题 python 最好下载2.6-2.7版本的 Python Releases for Windows | Python.org 不然下载代码会有问题&#xff0c;下不了&#xff0c;会提示安装2.6-2.7版本的 Windows下成功安…...

jsp进阶

文章目录jsp进阶内容回顾JSP 的九大内置对象内置对象的创建九大内置对象详解四大作用域对象四大作用域范围总结EL 进阶JSTL 标准标签库JSTL 核心标签jsp进阶 内容回顾 jsp 创建 jsp 的工作原理&#xff1a;翻译 --> 编译 --> 运行 翻译&#xff1a;第一次访问 jsp 页面…...

谷歌浏览器插件

项目中有时候会用到插件 sync-cookie-extension1.0.0&#xff1a;开发环境同步测试 cookie 至 localhost&#xff0c;便于本地请求服务携带 cookie 参考地址&#xff1a;https://juejin.cn/post/7139354571712757767 里面有源码下载下来&#xff0c;加在到扩展即可使用FeHelp…...

【Java学习笔记】Arrays类

Arrays 类 1. 导入包&#xff1a;import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序&#xff08;自然排序和定制排序&#xff09;Arrays.binarySearch()通过二分搜索法进行查找&#xff08;前提&#xff1a;数组是…...

在rocky linux 9.5上在线安装 docker

前面是指南&#xff0c;后面是日志 sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo dnf install docker-ce docker-ce-cli containerd.io -y docker version sudo systemctl start docker sudo systemctl status docker …...

SCAU期末笔记 - 数据分析与数据挖掘题库解析

这门怎么题库答案不全啊日 来简单学一下子来 一、选择题&#xff08;可多选&#xff09; 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘&#xff1a;专注于发现数据中…...

线程与协程

1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指&#xff1a;像函数调用/返回一样轻量地完成任务切换。 举例说明&#xff1a; 当你在程序中写一个函数调用&#xff1a; funcA() 然后 funcA 执行完后返回&…...

pam_env.so模块配置解析

在PAM&#xff08;Pluggable Authentication Modules&#xff09;配置中&#xff0c; /etc/pam.d/su 文件相关配置含义如下&#xff1a; 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块&#xff0c;负责验证用户身份&am…...

MODBUS TCP转CANopen 技术赋能高效协同作业

在现代工业自动化领域&#xff0c;MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步&#xff0c;这两种通讯协议也正在被逐步融合&#xff0c;形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...

相机从app启动流程

一、流程框架图 二、具体流程分析 1、得到cameralist和对应的静态信息 目录如下: 重点代码分析: 启动相机前,先要通过getCameraIdList获取camera的个数以及id,然后可以通过getCameraCharacteristics获取对应id camera的capabilities(静态信息)进行一些openCamera前的…...

GitHub 趋势日报 (2025年06月08日)

&#x1f4ca; 由 TrendForge 系统生成 | &#x1f310; https://trendforge.devlive.org/ &#x1f310; 本日报中的项目描述已自动翻译为中文 &#x1f4c8; 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...

什么?连接服务器也能可视化显示界面?:基于X11 Forwarding + CentOS + MobaXterm实战指南

文章目录 什么是X11?环境准备实战步骤1️⃣ 服务器端配置(CentOS)2️⃣ 客户端配置(MobaXterm)3️⃣ 验证X11 Forwarding4️⃣ 运行自定义GUI程序(Python示例)5️⃣ 成功效果![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/55aefaea8a9f477e86d065227851fe3d.pn…...