go语言的一些常见踩坑问题

开始之前,介绍一下​最近很火的开源技术,低代码。

作为一种软件开发技术逐渐进入了人们的视角里,它利用自身独特的优势占领市场一角——让使用者可以通过可视化的方式,以更少的编码,更快速地构建和交付应用软件,极大程度地降低了软件的开发、配置、部署和培训成本。

应用地址: https://www.jnpfsoft.com
开发语言:Java/.net

这是一个基于 Java Boot/.Net Core 构建的简单、跨平台快速开发框架。前后端封装了上千个常用类,方便扩展;采用微服务、前后端分离架构,集成了代码生成器,支持前后端业务代码生成,满足快速开发;框架集成了表单、报表、图表、大屏等各种常用的 Demo 方便直接使用;后端框架支持 Vue2、Vue3,平台即可私有化部署,也支持 K8S 部署。

从其他语言刚转入go语言的时候比较容易出现以下方面的问题:

  • 字符串string
  • interface断言
  • 切片slices
  • map
  • 控制结构(for、switch)
  • defer channel管道
  • sync同步机制
  • select+timer
1.字符串String(Split分割)

项目可能使用情况: 当使用string的split功能分割空字符串时,再进行数据库模糊查询时候,如下:

img

踩坑分析: 当对空字符串进行Split,将会返回一个包含一个空字符串的切片数组,数组长度为1,但是查询时由于空字符串会被过滤掉了该条件,会导致查询出来的数据不正确,甚至可能会是全表扫,由于查询所有数据可能会系统崩溃掉。

如何避坑: 使用前可以排除空字符串

img

2.interface断言

在项目中也会经常使用类型断言,当使用interface()转化成相对应的类型时,如果不恰当使用断言而导致panic,踩坑代码:

img

踩坑分析: golang中对于类型的断言,一定需要加上第二个参数ok判断,否则类型不一致的话直接panic退出 如何避坑: 增加第二个参数ok来判断

img

3.切片slice
3.1 容量问题

要注意在make切片的时候的参数设置,参数设置有问题很容易导致取下标值不是自己想象中的值,如下:

img

踩坑分析: 一般来说,slice的初始化为 make([]T, length, capacity)。 如果省略了capacity,默认capacity等于length。因此上面建了一个[]int类型的切片,长度和容量为3的[0,0,0]切片,因此通过append(s,1)会使slice扩容成6,并添加元素1进去。输出结果为:[0,0,0,1]

如何避坑:1.使用make([]T, length, capacity)补全参数;2.使用make([]T, length),则使用通过索引方式赋值,例如,s[0]=1

3.2 截取[:n]

在项目中可能会使用到切片截取功能,如下简单的代码,那么会出现什么问题呢?

img

踩坑分析: 因为切片的截取是引用关系,共有 2 个切片 a 和 b,截取了 a 的一部分赋值给了 b,两者存在着关联。图3-2-1 因此,虽然切片 a 只有底层数组中 0 和 1 两个索引位正在被使用,其余未使用的底层数组空间毫无作用,图3-2-2。但由于正在被引用,他们也不会被 GC,因此造成了内存泄露。

img

图3-2-1

img

图3-2-2

如何避坑: 可以通过拷贝的方式,同时将原有的切片或者数组释放。

img

4.map
4.1nil的map赋值问题

在项目中也经常使用到map,但是对于map的使用也很容易出错,比如,对一个nil的map进行赋值:

img

踩坑分析: 对未初始化的map变量,添加元素时会空指针panic,抛出错误:

img

如何避坑:往map添加元素时需要先分配内存。 例如 m := make(map[int]int)

4.2 判断map中的key是否存在

在使用map的key取值时,需要先判断key是否存在,踩坑代码:

img

如何避坑: 不能通过取出来的值来判断key是否存在map中。需要采用如下的形式:

  if _, ok := m[1]; !ok {print("key not exists")}
​
4.3map的遍历顺序问题

在使用map for循环时,也会出现一些踩坑问题,比如,判断map两次循环相同顺序的值是否一致。

img

踩坑分析: map的遍历时,golang会提前取一个随机数,把桶的遍历顺序随机化。因此,在程序中,不能依赖遍历的顺序。 如何避坑: 如果需要确保遍历顺序,一般需要自行维护一个额外的有序的数据结构。比如,使用list+sorts

img

4.4 map的并发读写

在使用map时需要注意,map写入和读取操作是否存在并发问题,特别是引用第三方库的时候,比较容易出现并发map操作的问题,比如:

img

踩坑分析: golang中的普通map不是线程安全的,如果并发读写,会导致panic。出现这样的错误:

img

如何避坑:不要并发读写map,也即不要在多个goroutine中同时对map进行读和写。如果一定要有读和写,可以使用sync.Map,但是sync.Map性能比较低,小心使用。

5.控制结构
5.1for循环取址问题

在项目中经常使用for循环进行遍历,但是很容易在指针类型上使用错误,比如:

img

踩坑分析: 因为在循环里创建的所有函数变量共享相同的变量,其实就是一个可访问的存储位置,而不是固定的值。 因此在for多次循环中,value的地址只有一个。比如,在上面的循环变量p中,在每次迭代中只给它分配了一个新值,而循环变量的地址在每次迭代中都是相同的,因此将存储相同的指针。因此,上面的遍历中,在循环之后,它将保存在最后一次迭代中分配的值。因此运行以上代码,输出如下,和预期不一样:

img

如何避坑

(1).在上面的case中不要使用指针

(2).在本地赋予一个临时指针,使用临时指针进行赋值,就不会被覆盖。

 for _, p := range persons {innerP := ppersonMap[p.name] = &innerP}
5.2 for必包问题

在项目中也经常使用for循环进行启动协程,在使用协程的时候,需要注意的for循环体中的变量也是一样,比如:

img

踩坑分析: 这个问题和上面的指针问题类似,因为for遍历非常快,所以当for遍历完毕后,v的值是最后的值。因此,在go闭包函数运行的时候,打印的全部都是最新的值。 如何避坑

在循环中的闭包,应该使用传参的方式,将变量传入函数中。这个时候会发生一次拷贝,因此,不会被其它的变量所覆盖:

for _, v := range s {go func(v string) {println(v)}(v)
}
​

或者使用临时变量,将循环体中值重新赋值给临时变量中:

for _, v := range s {tempV:=vgo func() {println(tempV)}()
} 
5.3 switch多个case问题

在项目中也会使用到switch,但是由于go语言跟其他的语言的switch,也很容易误以为多个case放在一起能够接着执行,如下:

package mainimport "fmt"func main() {i := 1switch i {case 1:case 2:fmt.Println("ok")}fmt.Println("end")
}
​

踩坑分析: golang的switch和其它语言差别很大。像Java/c等,上面的情况可能使case 1和case 2都执行到了下面的语句。但是golang会自动为每个case增加break。 因此,上面执行到了case 1之后就退出了。 如何避坑:如果需要上面的case满足预期,可以在case1后面增加fallthrougth语句。 或者直接case1, 2多个条件一起。

package mainimport "fmt"func main() {i := 1switch i {case 1:fallthrougthcase 2:fmt.Println("ok")fallthrougth}fmt.Println("end")
}
​
6.defer问题
6.1 defer在跨协程的问题

在项目中defer经常在使用func方法最前面,进行捕获一些非法异常,但是也很容易忽略了跨协程的问题,比如:

//PublishBusiness 发布 
func PublishBusiness(ctx context.Context, businessId int64) error {var e errordefer func() {if e !=nil{logger.CtxLogErrorf(ctx, "PublishBusiness err: %v", err)
​}}()//更新e = b.doPublishBusiness(c, businessId)go func() {1/0 //子协程 pianc}() return err
}
​

踩坑分析: defer 只会在当前函数返回前执行传入的函数,理解这句话主要在三个方面:当前函数返回前执行传入的函数,即 defer 关键值后面跟的是一个函数,包括普通函数如(fmt.Println), 也可以是匿名函数 func() 因此,在使用recover时,必须在同一个goroutine中使用才可以捕获panic。上面出现panic是在子goroutine中,因此无法捕获,会导致程序crash中断退出。 如何避坑:一般启动一个goroutine时,必须在该goroutine中处理panic,使用defer捕获一下。

6.2 循环中使用defer

在项目中会使用到for循环打开文件,但是在关闭文件的时候容易出现问题,比如:

package mainimport ("log""os"
)func main() {for i := 0; i < 10; i++ {f, err := os.Open("/path/file")if err != nil {log.Fatalln(err)}defer f.Close()}
}
​

踩坑分析: 因为defer是在整个函数运行完毕之后才会执行。因此上面的代码中,会出现内存泄漏问题,因为在循环中,每个defer函数会压入到堆栈中。等到整个main函数执行完毕,才从堆栈中弹出来defer函数进行执行。假如循环比较大,而且里面的执行比较重,那么会严重影响性能。

如何避坑:不要再for循环中使用defer函数。可以通过匿名函数将函数快速结束,从而快速执行defer函数释放资源。例如:

package main
import ("log""os"
)
func main() {for i := 0; i < 10; i++ {func() {f, err := os.Open("/path/file")if err != nil {log.Println(err)return}defer f.Close()}()}
}
​
7.channel管道问题
7.1 channel管道panic的问题

项目经常使用协程并发,结果收集会集中在channel管道中,但在channe使用也比较容易出问题,比如:

import "time"func main() {ch := make(chan int)go func() {for i := 0; i < 1000; i++ {ch <- itime.Sleep(1)}}()go func() {close(ch)}()time.Sleep(100000)
}

踩坑分析: 在channel错误操作比较容易影响panic,下面几类:a).向已关闭的channel发送数据导致panicb).重复关闭channel会导致panicc).关闭nil channel会导致panic因此在上面的例子就是向已关闭的channel发送数据导致panic,会导致程序不可用 如何避坑: channel关闭要适当,也不要向关闭的channel中进行操作,包括发送信息,再次关闭等

7.2 channel管道死锁的问题

因为在channel存在生产者和消费者,也容易出现问题,比如:

 package mainfunc main() {ch := make(chan int)ch <- 1<-ch}
​

踩坑分析: 造成死锁的原因:循环等待、资源共享、非抢占式, 在并发中出现通道死锁有两种情况:数据要发送,但是没有人接收数据要接收,但是没有人发送 因此上面就是,因为生产者和消费者在同一个goroutine中,因此无法并行执行,导致发送的消息一直无法被消费掉,而在ch<- 1一直阻塞着,出现死锁。 如何避坑: 生产者和消费者不能属于同一个goroutine,且生成者和消费者应该成对出现

8.sync同步机制panic问题

在并发下,sync同步机制也经常使用,也是比较容易出现问题的,比如,sync.Mutex:

package mainimport "sync"func main() {var r sync.Mutexr.Lock()r.Unlock()r.Unlock()
}
​

踩坑分析: 在同步机制上造成panic会有以下情况: a).sync.Mutex 没有加锁就进行解锁而导致panic b).sync.Mutex 重复解锁而导致panic c).sync.WaitGroup 计数为负而导致panic 因此上面就是,sync.Mutex 重复解锁而导致panic 如何避坑: 加锁和解锁配对出现

select+timer

项目中在一些情况下需要进行超时控制,使用select+timer去解决超时控制,这边也会有一个坑,比如:

package main
import ("fmt""time"
)func main() {ch := make(chan string)go func() {for i := 0; i < 100; i++ {ch <- "ok"}}()for {select {case v := <-ch:fmt.Println(v)case <-time.After(time.Second * 10):fmt.Println("timeout")}}
}
​

踩坑分析: 在for循环每次select的时候,都会实例化一个一个新的定时器。该定时器在10秒后 ,才会被激活,但是激活后已经跟select无引用关系,被gc给清理掉。 换句话说,被遗弃的time.After定时任务还是在时间堆里面,定时任务未到期之前,是不会被gc清理的。因此,会出现内存泄漏的现象。

如何避坑

a).改为timer的方式:

ticker := time.NewTicker(3 * time.Second)
for {<-ticker.Cfmt.Println("timeout")
}
​

b).使用context.WithTimeout方式:

ctx, cancel := context.WithTimeout(ctx, 3*time.Second)defer cancel() select {case <-ch:return truecase <-ctx.Done():return false
}
​

go​编辑程序员

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【无重复字符的最长子串】python,滑动窗口+哈希表

滑动窗口哈希表 哈希表 seen 统计&#xff1a; 指针 j遍历字符 s&#xff0c;哈希表统计字符 s[j]最后一次出现的索引 。 更新左指针 i &#xff1a; 根据上轮左指针 i 和 seen[s[j]]&#xff0c;每轮更新左边界 i &#xff0c;保证区间 [i1,j] 内无重复字符且最大。 更新结…

JVM学习-垃圾回收器(一)

垃圾回收器 按线程数分类 串行垃圾回收器 串行回收是在同一时间段内只允许有一个CPU用于执行垃圾回收操作&#xff0c;此时工作线程被暂停&#xff0c;直至垃圾收集工作结束 在诸如单CPU处理器或者较小的应用内存等硬件平台不是特别优越的场合&#xff0c;串行回收器的性能表…

http和https的区别,怎么免费实现https(内涵教学)

超文本传输协议HTTP协议被用于在Web浏览器和网站服务器之间传递信息&#xff0c;HTTP协议以明文方式发送内容&#xff0c;不提供任何方式的数据加密&#xff0c;如果攻击者截取了Web浏览器和网站服务器之间的传输报文&#xff0c;就可以直接读懂其中的信息&#xff0c;因此&…

etcd 和 MongoDB 的混沌(故障注入)测试方法

最近在对一些自建的数据库 driver/client 基础库的健壮性做混沌&#xff08;故障&#xff09;测试, 去验证了解业务的故障处理机制和恢复时长. 主要涉及到了 MongoDB 和 etcd 这两个基础组件. 本文会介绍下相关的测试方法. MongoDB 中的故障测试 MongoDB 是比较世界上热门的文…

AI网络爬虫:批量爬取电视猫上面的《庆余年》分集剧情

电视猫上面有《庆余年》分集剧情&#xff0c;如何批量爬取下来呢&#xff1f; 先找到每集的链接地址&#xff0c;都在这个class"epipage clear"的div标签里面的li标签下面的a标签里面&#xff1a; <a href"/drama/Yy0wHDA/episode">1</a> 这个…

短视频矩阵系统4年独立开发正规代发布接口源码搭建部署开发

1. 短视频矩阵源码技术开发要求及实现流程&#xff1a; 短视频矩阵源码开发要求具备视频录制、编辑、剪辑、分享等基本功能&#xff0c;支持实时滤镜、特效、音乐等个性化编辑&#xff0c;能够实现高效的视频渲染和处理。开发流程主要包括需求分析、技术选型、设计架构、编码实…

Web前端开发技术、详细文章、(例子)html 列表、有序列表、无序列表、列表嵌套

目录 列表概述 列表类型与标记符号 无序列表 语法&#xff1a; 语法说明&#xff1a; 无序列表标记的 type 属性及其说明 代码解释 有序列表 基本语法 属性说明 1、列表 o1标记的属性 2、列表项li标记的属性 有序列表 o1标记的属性、值 代码解释 列表嵌套 基本…

FreeBSD/Linux下的系统资源监视器排队队

bpytop bpytop 是一个基于 Python 的资源监视器&#xff0c;可以在 FreeBSD 上使用。它提供了对文件写入磁盘、网络、CPU 和内存占用的监视功能。 pkg install bpytop 或者用ports安装 cd /usr/ports/sysutils/bpytop/ make install clean bashtop bashtop 也是一个基于 P…

化简资源分配图判断是否发生死锁

目录 1.资源分配图的概念 2.判断是否发生死锁 1.资源分配图的概念 资源分配图表示进程和资源之间的请求关系&#xff0c;例如下图&#xff1a; P代表进程&#xff0c;R代表资源&#xff0c;R方框中 有几个圆球就表示有几个这种资源&#xff0c;在图中&#xff0c;R1指向P1&a…

C++ RPC ORM 高速解析

支持所有常用编程语 https://capnproto.org/GitHub - capnproto/capnproto: Capn Proto serialization/RPC system - core tools and C library https://capnproto.org/capnproto-c-win32-1.0.2.zip 常用命令&#xff1a; capnp help capnp compile -oc myschema.capn…

Excel中sum的跨表求和

#实际工作中&#xff0c;一个xlsx文件中会包含多个Excel表格&#xff0c;一般会有“总-分”的关系&#xff0c;如何把分表里的数字汇总到总表里呢&#xff1f; 一般有上图所示的两种表达方式。 可以使用通配符 *&#xff1a;代表任意个数、任意字符&#xff1b; &#xff1f;&…

quartz定时任务

Quartz 数据结构 quartz采用完全二叉树&#xff1a;除了最后一层每一层节点都是满的&#xff0c;而且最后一层靠左排列。 二叉树节点个数规则&#xff1a;每层从左开始&#xff0c;第一层只有一个&#xff0c;就是2的0次幂&#xff0c;第二层两个就是2的1次幂&#xff0c;第三…

DOS学习-目录与文件应用操作经典案例-attrib

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一.前言 二.使用 三.案例 一.前言 DOS系统中的attrib命令是一个用于显示或更改文件&#…

设计模式——职责链(责任链)模式

目录 职责链模式 小俱求实习 结构图 实例 职责链模式优点 职责链模式缺点 使用场景 1.springmvc流程 ​2.mybatis的执行流程 3.spring的过滤器和拦截器 职责链模式 使多个对象都有机会处理请求&#xff0c;从而避免请求的发送者和接受者之间的耦合关系。将这个对象连成…

github设置项目分类

https://www.php.cn/faq/541957.html https://docs.github.com/zh/repositories/working-with-files/managing-files/creating-new-files

什么是回表,如何解决回表问题

下面表中:主键id是聚簇索引&#xff0c;name是辅助索引。 执行这样一条SQL: select name from A where name"s;name字段是有索引&#xff0c;所以MYSQL在通过name进行査询的时候&#xff0c;是需要扫描两颗Btree树的。 第一遍:先通过二级索引定位主键值1。第二遍:根据主键…

免费发布web APP的四个途径(Python和R)

免费发布数据分析类&#x1f310;web APP的几个途径&#x1f4f1; 数据分析类web APP目前用来部署生信工具&#xff0c;统计工具和预测模型等&#xff0c;便利快捷&#xff0c;深受大家喜爱。而一个免费的APP部署途径&#xff0c;对于开发和测试APP都是必要的。根据笔者的经验…

word-形状绘制、smartart、visio

一、人员架构图绘制 小技巧&#xff1a; 1、ctrlshift水平复制 2、点击图形&#xff0c;右键设置为默认形状 3、插入-形状-右键-锁定绘图模式&#xff0c;按esc退出状态 4、插入-形状-新建绘图画布&#xff0c;代替组合问题 画布中存在锚点&#xff0c;便于直线连接 二、s…

深度学习之基于YOLOV5的口罩检测系统

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 一、项目背景 随着全球公共卫生事件的频发&#xff0c;口罩成为了人们日常生活中不可或缺的一部分。在公共场所&am…

10、SpringBoot 源码分析 - 自动配置深度分析三

SpringBoot 源码分析 - 自动配置深度分析三 refresh和自动配置大致流程AutoConfigurationImportSelector的getAutoConfigurationEntry获取自动配置实体(重点)AutoConfigurationImportSelector的getCandidateConfigurations获取EnableAutoConfiguration类型的名字集合AutoConfig…