Loading [MathJax]/jax/input/TeX/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >深入理解 Go 中的 defer、panic 、日志管理与WebAssembly

深入理解 Go 中的 defer、panic 、日志管理与WebAssembly

作者头像
蒙娜丽宁
发布于 2024-11-23 07:44:06
发布于 2024-11-23 07:44:06
8820
代码可运行
举报
文章被收录于专栏:极客起源极客起源
运行总次数:0
代码可运行

延迟执行 (defer) 关键字的使用

在 Go 语言中,defer 关键字用于推迟某个函数的执行,直到其所在的外层函数即将返回时才执行。这在文件输入输出操作中非常有用,因为它允许你在打开文件后直接将关闭文件的操作放在附近,从而避免忘记关闭文件。defer 可以让你的代码更加简洁、可读。虽然在后续章节中我们将讨论 defer 在文件操作中的应用,本文先介绍 defer 在其他场景中的两种用法。

defer 的执行顺序

一个非常重要的点是,defer 语句会按照后进先出的顺序(LIFO)执行。这意味着,如果你在同一个函数中依次 deferf1()f2()f3(),那么在函数返回时,f3() 将会先执行,接着是 f2(),最后是 f1()

为了更好地理解 defer 的工作机制,下面是一个简单的 Go 代码示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package main import (     "fmt" )  func d1() {     for i := 3; i > 0; i-- {         defer fmt.Print(i, " ")     } } 

除了 import 块外,上面的代码实现了一个名为 d1() 的函数,其中包含一个 for 循环和一个 defer 语句。defer 将会在循环体内执行三次。

接下来是程序的第二部分:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
func d2() {     for i := 3; i > 0; i-- {         defer func() {             fmt.Print(i, " ")         }()     }     fmt.Println() } 

在这个部分的代码中,你可以看到另一个名为 d2() 的函数实现。它同样包含一个 for 循环和一个 defer 语句,但这次 defer 应用于一个匿名函数,而不是直接调用 fmt.Print()。匿名函数没有参数,因此每次循环都会捕获 i 的当前值。

最后一部分代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
func d3() {     for i := 3; i > 0; i-- {         defer func(n int) {             fmt.Print(n, " ")         }(i)     } }  func main() {     d1()     d2()     fmt.Println()     d3()     fmt.Println() } 

在这个部分,main() 函数调用了 d1()d2()d3() 函数。在 d3() 中,匿名函数带有一个参数 n,并且在每次 defer 时,将 i 的当前值传递给了该匿名函数。执行整个程序时,输出如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1 2 3  0 0 0  1 2 3 

你可能觉得这个输出很难理解,因为 defer 的操作和结果可能有些让人迷惑。我们来解释一下这些输出,以帮助你更好地理解。

结果分析

首先,输出的第一行 1 2 3 是由 d1() 函数生成的。在 d1() 中,i 的值按顺序是 3、2、1,但由于 defer 的执行顺序是 LIFO,因此在 d1() 返回时,值按相反顺序输出。

接下来是由 d2() 生成的第二行输出 0 0 0。为什么不是 1 2 3?原因在于,for 循环结束时,i 的值为 0,而匿名函数是在 for 循环结束后才执行的,因此 i 的值为 0 时,匿名函数被执行了三次,结果是三个 0。

最后,第三行 1 2 3 是由 d3() 生成的。因为匿名函数带有参数 n,每次 deferi 的值会被传递给匿名函数,因此 defer 的匿名函数捕获了不同的 i 值,输出了正确的顺序 1 2 3

因此,最好的 defer 使用方法是像 d3() 那样,通过显式传递所需的参数来避免混淆。

日志中的 defer 使用

defer 还可以应用于日志记录,帮助你在程序中更好地组织日志信息。通过在函数开头和返回前分别记录开始和结束日志,你可以确保所有日志输出都是成对的。这样可以让日志信息更加清晰,易于查找。

例如,以下代码展示了如何使用 defer 记录函数的开始和结束日志:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package main import (     "fmt"     "log"     "os" )  var LOGFILE = "/tmp/mGo.log"  func one(aLog *log.Logger) {     aLog.Println("-- 函数 one 开始 --")     defer aLog.Println("-- 函数 one 结束 --")     for i := 0; i < 10; i++ {         aLog.Println(i)     } } 

这个 one() 函数使用了 defer,确保第二个 aLog.Println() 在函数返回前被执行,因此日志输出会被封装在两个日志调用之间,使得日志信息更具可读性。

接下来是另一个类似的函数 two()

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
func two(aLog *log.Logger) {     aLog.Println("---- 函数 two 开始 ----")     defer aLog.Println("-- 函数 two 结束 --")     for i := 10; i > 0; i-- {         aLog.Println(i)     } } 

two() 函数也使用了 defer 来组织日志信息,这次的日志内容略有不同,但原理相同。

最后,我们看看 main() 函数的实现:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
func main() {     f, err := os.OpenFile(LOGFILE, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)     if err != nil {         fmt.Println(err)         return     }     defer f.Close()     iLog := log.New(f, "logDefer ", log.LstdFlags)     iLog.Println("程序开始!")     one(iLog)     two(iLog)     iLog.Println("程序结束!") } 

这里,我们打开了一个日志文件,并使用 defer 确保文件在程序结束时被关闭。运行这个程序并查看日志文件的内容,你会发现以下输出:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
logDefer 2019/01/19 21:15:11 -- 函数 one 开始 -- logDefer 2019/01/19 21:15:11 0 logDefer 2019/01/19 21:15:11 1 ... logDefer 2019/01/19 21:15:11 -- 函数 one 结束 -- logDefer 2019/01/19 21:15:11 ---- 函数 two 开始 ---- logDefer 2019/01/19 21:15:11 10 logDefer 2019/01/19 21:15:11 9 ... logDefer 2019/01/19 21:15:11 -- 函数 two 结束 -- 

这样,通过 defer,日志信息可以成对显示,使日志更加清晰,便于调试。

panicrecover

接下来,我们讨论一个稍微复杂点的机制:panic()recover()panic() 是 Go 语言中的内建函数,它会中断当前程序的正常执行,并进入恐慌状态。而 recover() 则允许你在发生恐慌后重新获得控制权。

以下是一个展示这两者使用的示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package main import "fmt"  func a() {     fmt.Println("进入 a()")     defer func() {         if c := recover(); c != nil {             fmt.Println("在 a() 中恢复!")         }     }()     fmt.Println("即将调用 b()")     b()     fmt.Println("b() 已退出!") }  func b() {     fmt.Println("进入 b()")     panic("b() 中的恐慌!") }  func main() {     a()     fmt.Println("main() 已结束!") } 

运行这段代码会得到以下输出:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
进入 a() 即将调用 b() 进入 b()a() 中恢复! main() 已结束! 

在这个例子中,b() 中调用了 panic(),但由于 a() 中有一个 recover(),程序得以从恐慌中恢复,并且继续执行剩下的代码。

使用 panic() 处理错误

在某些情况下,你可能只想使用 panic() 来强制终止程序。以下代码

展示了这种情况:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package main import (     "fmt"     "os" )  func main() {     if len(os.Args) == 1 {         panic("参数不足!")     }     fmt.Println("感谢提供参数!") } 

当没有提供命令行参数时,程序将输出以下内容并中止:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
panic: 参数不足! 

panic() 是一种直接处理错误的方式,但请记住,如果不使用 recover()panic() 会使程序立即崩溃。

UNIX 调试工具

当程序出现问题时,有时我们不希望通过修改代码来添加大量的调试信息。这时可以借助 UNIX 下的工具,如 stracedtrace,来跟踪程序的系统调用并找出问题所在。

strace 工具

strace 是一个用于跟踪 Linux 系统中系统调用和信号的工具。你可以使用它来查看某个程序在运行时所执行的系统调用。例如,运行 strace ls 会输出如下内容:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
execve("/bin/ls", ["ls"], [/* 15 vars */]) = 0 
dtrace 工具

dtrace 是 macOS 和 FreeBSD 系统中的另一个强大工具,允许你监视系统中正在运行的程序而无需修改代码。例如,使用 dtruss godoc 命令可以跟踪 godoc 程序的系统调用。

检查 Go 语言环境

Go 语言提供了 runtime 包,用于查看当前 Go 环境的信息。以下代码展示了如何使用 runtime 获取系统信息:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package main import (     "fmt"     "runtime" )  func main() {     fmt.Println("使用的编译器:", runtime.Compiler)     fmt.Println("系统架构:", runtime.GOARCH)     fmt.Println("Go 语言版本:", runtime.Version())     fmt.Println("CPU 数量:", runtime.NumCPU())     fmt.Println("当前 Goroutines 数量:", runtime.NumGoroutine()) } 

运行这段代码,你可以得到当前使用的编译器、系统架构、Go 版本等信息。

WebAssembly 的生成与使用

Go 支持将代码编译为 WebAssembly(Wasm),这是一种面向虚拟机的高效执行格式,适用于多种平台。以下是一个简单的 Go 代码示例,它将会被编译为 WebAssembly:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package main import (     "fmt" )  func main() {     fmt.Println("生成 WebAssembly 代码!") } 

使用以下命令将其编译为 WebAssembly:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ GOOS=js GOARCH=wasm go build -o main.wasm toWasm.go 

生成的 main.wasm 文件可以在支持 WebAssembly 的浏览器中运行。你还需要加载 wasm_exec.js 文件,来帮助浏览器运行 WebAssembly。

以下是一个简单的 index.html 文件,包含用于加载和运行 WebAssembly 的代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<!DOCTYPE html> <html> <head>     <meta charset="utf-8">     <title>Go 和 WebAssembly</title> </head> <body>     <script src="wasm_exec.js"></script>     <script>         const go = new Go();         WebAssembly.instantiateStreaming(fetch("main.wasm"), go.importObject).then((result) => {             go.run(result.instance);         });     </script> </body> </html> 

编写高质量的 Go 代码的建议

本文最后总结了一些实用的建议,帮助你编写高质量的 Go 代码:

  1. 当函数中出现错误时,要么记录错误,要么返回错误,不要同时做这两件事,除非有特殊理由。
  2. Go 接口定义的是行为,而不是数据。
  3. 使用 io.Readerio.Writer 接口,使代码更具扩展性。
  4. 只有在必要时才传递变量的指针,其他时候直接传递值。
  5. 错误类型不是字符串,它是 error 类型。
  6. 不要在生产环境中测试代码,除非有特殊理由。
  7. 如果不熟悉某个 Go 特性,先做测试再用,尤其是大规模应用时。
  8. 不要害怕犯错,尽量多做实验,实践是最好的学习方式。

- EOF -

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-09-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 极客起源 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
2 条评论
热度
最新
可以给个体验地址吗?
可以给个体验地址吗?
回复回复点赞举报
@乔戈里 后台有链接和账号吗?就是PC端
@乔戈里 后台有链接和账号吗?就是PC端
回复回复点赞举报
推荐阅读
编辑精选文章
换一批
【程序源代码】物业管理系统
  “ 关键字:  “java web 餐厅订座小程序" 01 ———— 【总体介绍】 物业管理系统       这是一整套开源智慧物业解决方案,基于nodejs、typescript、koa、vue开发,包含web中台、业主小程序、员工小程序、公众号、物联网应用等,涵盖业主服务、物业运营、智能物联、数据统计等主要业务。 02 ———— 【安装使用】 技术架构 自主独立研发,无阉割、无加密、真正的开源 web中台页面采用view-design开发,清爽易用 轻量级开发方案,借助node.js的高io
程序源代码
2022/05/25
1.7K0
【程序源代码】物业管理系统
[开源]完全开源免费商用,为你们小区物业企业打造的智慧物业综合管理平台
地址:https://gitee.com/wuxw7/MicroCommunity
开源日记
2023/03/10
2.9K0
[开源]完全开源免费商用,为你们小区物业企业打造的智慧物业综合管理平台
持续发布公众号文章后终于吸引到同频的人找我一起合作做个小区智慧物业系统!
最近几乎每隔一天笔者就会发布一篇公众号文章,很明显的正向反馈也来了,一方面是公众号每天都有新的粉丝添加关注,每天的流量费也在慢慢地涨。最令我惊喜的是上周三把一篇刚公众号文章SpringBoot 整合 Vue3 与 Element-Plus 完成系统页面表单和表格功能开发 分享到自己的朋友圈后,就有一个之前给我推荐工作的技术小哥找我一起做一个小区智慧物业系统。
用户3587585
2024/05/20
2790
持续发布公众号文章后终于吸引到同频的人找我一起合作做个小区智慧物业系统!
分析:为什么物业社区版小程序云平台有很大机会
目前国内大部分房地产和物业企业通过搭建社区O2O渠道进行社区增值服务(主要体现在社区新零售方面)。而根据经济观察报的采访,业内很多营销公关做得很好的社区APP,实际上后台数据很难看。“注册用户数量少,注册后登陆使用过的只占几百几千分之一,更不要提反复使用,形成黏性”。当然这也成为大家“心照不宣”的共识。
城镇云
2020/06/09
9300
分析:为什么物业社区版小程序云平台有很大机会
开源轻量级,高性能,前后端分离的电商系统,支持微信小程序+H5+公众号+APP
萤火商城 v2.0 版,是 2021 年全新推出的一款轻量级、高性能、前后端分离的电商系统,支持微信小程序 + H5+ 公众号 + APP,前后端源码 100% 开源,看见及所得,完美支持二次开发,让您快速搭建个性化独立商城。 项目介绍 萤火商城 V2.0,是 2021 年全新推出的一款轻量级、高性能、前后端分离的电商系统,支持微信小程序 + H5+ 公众号 + APP,前后端源码完全开源,看见及所得,完美支持二次开发,可学习可商用,让您快速搭建个性化独立商城。 技术特点 前后端完全分离 (互不依赖 开发效
开源日记
2023/03/10
8860
开源轻量级,高性能,前后端分离的电商系统,支持微信小程序+H5+公众号+APP
从我两年前倒闭的小网站,聊聊如何做一个网站
大家好,我是程序员鱼皮。最近我的毛发日渐稀疏啊,都是因为在搞新项目 面试鸭。这是一个面试刷题小程序,能帮大家用最快的速度刷题备战面试、并学到技术。
程序员鱼皮
2024/07/18
3490
从我两年前倒闭的小网站,聊聊如何做一个网站
下周,我倒闭 2 年的小网站将重出江湖!
大家好,我是程序员鱼皮。最近我的毛发日渐稀疏啊,都是因为在搞新项目 面试鸭。这是一个面试刷题小程序,能帮大家用最快的速度刷题备战面试、并学到技术。
程序员鱼皮
2024/07/20
2150
下周,我倒闭 2 年的小网站将重出江湖!
深度好文 | 你一定不知道,微信里竟然有这么多赚钱的路子 ...
现如今的微信,已经远远不止是社交工具了。有人说微信正在变成操作系统,承载了各种应用,吞噬了用户大量的时间。其实,不管微信本身发展成什么样子,在这个月活超过 10 亿的航母周围,总是有生意可以做的。那么今天,我们就来看看围绕微信的那些生意,以及他们的生财之道。希望通过本文,能够让大家对微信的次级生态有个整体概念,相信其中一定蕴藏着更多巨大的商机。
姬小光
2018/09/05
5.7K0
深度好文 | 你一定不知道,微信里竟然有这么多赚钱的路子 ...
腾讯企点联合贝恩公司共同发布《360°智慧客户运营白皮书》
过去20年,消费互联网市场繁荣,许多企业通过“跑马圈地”的方式快速拓展增量市场,获得客户资源及市场份额。如今,在日益复杂的宏观经济和营商环境下,获客难度不断攀升,企业应当转变增长思维:从“拉新获客”转向“深耕客户”,以实现长期且可持续增长。 值此转型变革之际,腾讯企点与贝恩公司于今日腾讯全球数字生态大会【智慧服务与营销】专场共同推出《 360°智慧客户运营》白皮书(文末下载),立足“未来回溯”理念,以终为始,帮助企业思考并规划未来客户运营愿景。 在今天的专场中,业内大咖围绕“数字化服务营销对于企业可持续
腾讯企点
2020/09/11
7020
一文为你揭秘深圳机场智慧航旅服务中的黑科技
为了提升深圳机场的整体服务效率,腾讯云为深圳机场构建了一套【OneID全流程旅客出行服务系统】。顾名思义,OneID,也就是一个旅客ID,贯穿旅客在机场的出港、进港、中转、接送机四大类出行场景,提供包括室内导航、交通出行、智能客服、行李跟踪等31个子服务。
腾讯云开发TCB
2020/03/31
1.3K0
干货|揭秘2020年最新的24招小程序运营玩法
小程序面世三年,在电商方面,为企业创造了超过100亿的gmv,而商家们该如何落地布局,抢占小程序电商红利呢?今天,新爷从我们服务过的众多商户中,总结出了24招高流量、高裂变、高转化、高复购的最新运营玩法,给电商企业以借鉴,让你赢在2020!
云店加小程序分享
2020/04/30
2.3K0
完整版web前端学习路线图(超详细自学路线)
JavaScript基础+进阶 ➾ Ajxa ➾ JavaScript应用项目实践 ➾ Node.js ➾ MongoDB项目实践
程序狗
2023/03/27
3.7K0
“小程序”:成就新零售的“大世界”
2017年1月9日小程序正式发布,五个月时间,动作频繁!大部分企业甚至包括很多业内人士,对小程序的认识也只是一个模糊概念,小程序将给企业带来什么? 如果说一个月前微信推出“搜一搜”和“看一看”时,
静一
2018/03/27
1.3K2
“小程序”:成就新零售的“大世界”
B端客户运营增长之“客户线索”
在B端产品得到市场验证后,即完成匹配,下一步动作便是获取更多的企业客户,但往往这一步难度颇高,很多成长性企业就是在这一步止步不前或者直接夭折,企业融资也融到B轮、C轮甚至D轮,但最后也只能止步上市前夜。
大军读财
2023/01/30
6180
B端客户运营增长之“客户线索”
DNSPod十问袁志远:智慧园区,被严重低估的To B赛道?
广东飞企互联科技股份有限公司联合创始人&总经理,中国智慧城市专家(库)委员会专家委员,广东省产业园区协会副会长。拥有21年IT企业运营管理、11年产业园区运营管理经验,带领公司人员成功在全国600多个智慧园区完成项目策划、建设、运营工作,曾获中国产业园区杰出人物贡献奖。 人称奶罩,腾讯云中小企业中心总经理,DNSPod创始人,洋葱令牌创始人,网络安全专家,域名及DNS技术专家,知名个人站长,中欧国际工商学院EMBA。 1 吴洪声:现在人人都在谈产业互联网,但其实一千个人心里有一千个“产业
腾讯云DNSPod团队
2021/11/22
7080
React的移动端和PC端生态圈的使用汇总
对于一项技术,我们不能停留在五分钟状态,特别喜欢一句话,用什么方式绘制UI界面一点不重要,重要的是底层的思维,解决问题和优化的思路。
桃翁
2019/07/15
2.4K0
React的移动端和PC端生态圈的使用汇总
企点营销特别策划|全链路私域运营,助力企业服务营销一体化闭环
前言:2020年的大部分时间里,B2B营销人对预测当下都无从下手。当疫情防控变成社会生活常态,移动互联网的渗透日趋饱和,流量红利逐渐消失,企业在公域流量上低成本获客拉新已经不再容易。通过技术创新,企业必须更加重视私域流量的运营, 提升客户满意度实现关系驱动型的营销增长。 在困难时期,客户的留存与增购对业务增长更为重要,因此基于目标客户的精细化运营,覆盖获客、识客、孵化、转化全生命周期的客户运营会成为业务增长基石。 立足现在展望未来,后流量时代的营销与企业的增长进入精细化存量运营时代。自200
腾讯企点
2021/07/02
1.5K0
“零接触”新需求,如何快速实现体温检测数字化管控方案?
特殊时期,社区等单位的管理提出了“零接触”新需求。如何基于物联网开发平台和腾讯连连小程序快速上线面向家庭/社区单位的产品服务,实现疫情管控“零接触”数字化管理?本文是周佳鑫老师在「云加社区沙龙online」的分享整理,详细带大家了解物联网项目开发流程、腾讯云物联网开发平台技术优势,以及如何借助loT Explorer和腾讯连连小程序快速开发上线新型体温检测产品。
腾讯云开发者
2020/03/25
2K0
老司机 iOS 周报 #98 | 2020-01-06
你也可以为这个项目出一份力,如果发现有价值的信息、文章、工具等可以到 Issues 里提给我们,我们会尽快处理。记得写上推荐的理由哦。有建议和意见也欢迎到 Issues 提出。
用户2932962
2020/01/15
8750
老司机 iOS 周报 #98 | 2020-01-06
腾讯企点总经理张晔:To B的核心是要为企业客户创造最大价值
由雷锋网主办的“鲸犀产业数字峰会”近日举行,这是一场由业内最顶尖的企业家、工程领袖、CIO、解决方案专家、投资家,联合发起的数字化系列论坛。 在峰会上,正式颁布了「产业升级年度十佳赋能者」,“腾讯企点”荣登榜单。同时,腾讯云副总裁、腾讯企点总经理张晔在峰会上,就以客户为中心的数字化升级做了分享。 产业数字化时代已经到来,To B企业存在的意义是什么?是为客户创造价值,还是变革产业? 张晔指出,企业存在的根本在于其为客户所创造的价值,如何运用先进的数字化技术、互联网技术帮助企业做业务流程创新、业
腾讯企点
2021/04/02
7550
推荐阅读
相关推荐
【程序源代码】物业管理系统
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验