13.Go 异常

news2024/11/17 16:29:17
1、宕机

Go语言的类型系统会在编译时捕获很多错误,但有些错误只能在运行时检查,如数组访问越界、空指针引用等,这些运行时错误会引起宕机。

一般而言,当宕机发生时,程序会中断运行,并立即执行在该goroutine(线程)中被延迟的函数(defer机制),随后,程序崩溃并输出日志信息。日志信息包括panic value和函数调用的堆栈跟踪信息,panicvalue通常是某种错误信息。

引发宕机有如下两种情况:

  • 程序主动调用panic()函数。
  • 程序产生运行时错误,由运行时检测并抛出。

发生panic后,程序会从调用panic的函数位置或发生panic的地方立即返回,逐层向上执行函数的defer语句,然后逐层打印函数调用堆栈,直到被recover捕获或运行到最外层函数而退出。

panic的参数是一个空接口类型interface{},所以,任意类型的变量都可以传递给panic。调用panic的方法非常简单,即panic (xxx)。

panic不但可以在函数正常流程中抛出,在defer逻辑中也可以再次调用panic或抛出panic。defer中的panic能够被后续执行的defer捕获。

Go语言可以在程序中手动触发宕机,让程序崩溃,这样开发者可以及时发现错误,同时减少可能的损失。

Go语言程序在宕机时,会将堆栈和goroutine信息输出到控制台,所以,宕机也可以方便地确定发生错误的位置。

func main() {
    panic("crash")
}


当panic()触发的宕机发生时,panic()后面的代码将不会被运行,但是在panic()函数前面已经运行过的defer语句依然会在宕机发生时发生作用:

func main() {
	defer fmt.Println("宕机时要做的事情1...")
	defer fmt.Println("宕机时要做的事情2...")
	panic("crash")
}

2、宕机恢复

无论代码运行错误是由Runtime层抛出的panic崩溃,还是主动触发的panic崩溃,都可以配合defer和recover实现错误的捕捉和恢复,让代码发生崩溃后允许继续运行。

recover()用来捕获panic,阻止panic继续向上传递。recover()函数可以和defer语句一起使用,但recover()函数只有在defer后面的函数体内被直接调用才能捕获panic终止异常,否则会返回nil,异常继续向外传递。

可以有连续多个panic被抛出,连续多个被抛出的场景只能出现在延迟调用中。虽然有多个panic被抛出,但是只有最后一次的panic才能被捕获:

func main() {
	defer func() {
		if err := recover(); err != nil {
			fmt.Println(err)
		}
	}()

	// 只有最后一次的panic调用能够被捕获
	defer func() {
		panic("first defer panic")
	}()

	defer func() {
		panic("second defer panic")
	}()
	panic("main body panic")
}

panic和recover的关系如下:

  • 有panic没recover,程序宕机。
  • 有panic也有recover,程序不会宕机,执行完对应的defer语句后,从宕机点退出当前函数后继续执行。
3、错误与处理

Go语言的错误处理过程如下:

  • 一个可能造成错误的函数,需要返回值中返回一个错误接口,如果调用是成功的,错误接口将返回nil,否则返回错误。
  • 在函数调用后需要检查错误,如果发生错误,则进行必要的错误处理。
a. 错误接口

error是Go语言系统声明的接口类型,语法格式:type error interface {Error() string}

所有符合Error()string格式的方法,都能实现错误接口,Error()方法返回错误的具体描述,使用者可以通过该字符串知道发生了什么错误。

Go语言内置错误接口类型error。任何类型只要实现Error() string方法,都可以传递error接口类型变量。Go语言典型的错误处理方式是将error作为函数最后一个返回值。在调用函数时,通过检测其返回的error值是否为nil来进行错误处理。

b. 自定义错误

返回错误前,需要定义会产生哪些可能的错误,在Go语言中,使用errors包进行错误的定义,格式如下:

var err = errors.New("this is an error")

错误字符串由于相对固定,一般在包作用域声明,应尽量减少在使用时直接使用errors.New返回。

errors 包:Go语言的errors中对New的定义非常简单

// 创建错误对象
func New(text string) error {
	return &errorString{text}
}

// 错误字符串
type errorString struct {
	s string
}

// 返回发生何种错误
func (e *errorString) Error() string {
	return e.s
}
  • 第2行:将errorString结构体实例化,并赋值错误描述的成员。
  • 第7行:声明errorString结构体,拥有一个成员,描述错误内容。
  • 第12行:实现error接口的Error()方法,该方法返回成员中的错误描述。

在代码中使用错误定义——例如:定义一个除法函数,当除数为0时,返回一个预定义的除数为0的错误。

// 定义除数为0的错误
var errDivisionByZero = errors.New("division by zero")

func div(dividend, divisor int) (int, error) {
	// 判断除数为0的情况并返回
	if divisor == 0 {
		return 0, errDivisionByZero
	}
	// 正常计算,返回空错误
	return dividend / divisor, nil
}

func main() {
	fmt.Println(div(1, 0))
}

c. 错误和异常

错误是指发生非期望的已知行为,这里的已知是指错误的类型是预料并定义好的。

异常是指发生非期待的未知行为。这里的未知是指错误的类型不在预先定义的范围内。异常又被称为未捕获的错误(untrapped error)。程序在执行时发生未预先定义的错误,程序编译器和运行时都没有及时将其捕获处理,而是由操作系统进行异常处理。

在Go语言中对于错误提供了两种处理机制:

  • 通过函数返回错误类型的值来处理错误。
  • 通过panic打印程序调用栈,终止程序执行来处理错误。

对错误的处理也有两种方法:

  • 一种是通过返回一个错误类型值来处理错误,
  • 另一种是直接调用panic抛出错误,退出程序。

Go是静态强类型语言,程序的大部分错误是可以在编译器检测到的,但是有些错误行为需要在运行期才能检测出来。此种错误行为将导致程序异常退出。其表现出的行为就和直接调用panic一样:打印出函数调用栈信息,并且终止程序执行。

在实际的编程中,error和panic的使用应该遵循以下原则:

(1)程序发生的错误导致程序不能容错继续执行,此时程序应该主动调用panic或由运行时抛出panic。

(2)程序虽然发生错误,但是程序能够容错继续执行,此时应该使用错误返回值的方式处理错误,或者在可能发生运行时错误的非关键分支上使用recover捕获panic。

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

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

相关文章

vue和react哪种框架使用范围更广

Vue和React都是非常流行的前端JavaScript框架,它们各自有着广泛的应用场景和支持者。选择使用哪一个框架往往取决于特定的项目需求、开发团队的熟悉程度以及生态系统的偏好。以下是这两个框架的一些主要特点,以帮助比较它们的使用范围: React…

基于Springboot的服务端开发脚手架-自动生成工具

继之前的 专题系列课程: ​​从零开始搭建grpc分布式应用​​完整DEMO:​​基于Springboot的Rpc服务端开发脚手架(base-grpc-framework)​​ 后带来一款项目自动手成工具(由于包路径等原因,完整demo想应用在实际开发中需要改很多代…

git使用(完整流程)

1. 新建仓库 1.右击 git bash 后 输入 git init (仓库为:当前目录) git init name (仓库为:name文件夹) git clone https://github.com/Winnie996/calculate.git //https2.工作区域 工作目录 3. 添加 提交 git add . //工作区添加至暂存区 git commit -m "注释内容&q…

免费邮件系统hMailServer本地部署并实现远程发送邮件

文章目录 前言1. 安装hMailServer2. 设置hMailServer3. 客户端安装添加账号4. 测试发送邮件5. 安装cpolar6. 创建公网地址7. 测试远程发送邮件8. 固定连接公网地址9. 测试固定远程地址发送邮件 前言 hMailServer 是一个邮件服务器,通过它我们可以搭建自己的邮件服务,通过cpola…

Python算法例32 统计数字

1. 问题描述 计算数字k在0~n中出现的次数,k可能是0~9中的一个数字。 2. 问题示例 n12,k1,在[0,1,2,3,4,5,6,7,8,9,10&a…

野生菌行业第一品牌,让世界爱上“野生菌,好宝贝”

近年来,随着人们对健康和美食的追求不断升温,野生菌作为一种珍贵的食材备受瞩目。在这个行业中,一个名为菌贝的品牌正在崭露头角。作为专注于野生菌产业和产品的领军品牌,菌贝由互联网知名人士贤哥、付颖和飘云等创始人于2018年10…

高压继电器,未来几年市场将保持稳定增长

高压继电器是一种用于控制大功率电气设备的开关装置,广泛应用于电力系统、轨道交通、工业自动化等领域。随着各行业对电气控制需求的不断增加,高压继电器市场也在不断扩大。全球高压继电器市场分析: 在全球市场中,目前主要的高压继…

MidTool图文创作-GPT-4与DALL·E 3的结合

GPT-4与DALLE 3的结合 GPT-4是由OpenAI开发的最新一代语言预测模型,它在前代模型的基础上进行了大幅度的改进,不仅在文本生成的连贯性、准确性上有了显著提升,还在理解复杂语境和执行多步骤指令方面表现出了更高的能力。而DALLE 3则是一个创…

Flutter 混合开发 - 动态下发 libflutter.so libapp.so

背景 最近在做包体积优化,在完成代码混淆、压缩,裁剪ndk支持架构,以及资源压缩(如图片转webp、mp3压缩等)后发现安装包的中占比较大的仍是 so 动态库依赖。 具体查看发现 libflutter.so 和 libapp.so 的体积是最大的&…

solidity显示以太坊美元价格

看过以太坊白皮书的都知道,以太坊比较比特币而言所提升的地方中,我认为最重要的一点就是能够访问外部的数据,这一点在赌博、金融领域应用会很广泛,但是区块链是一个确定的系统,包括里面的所有数值包括交易ID等都是确定…

pyqt6 + pycharm 搭建+使用入门

首先安装PyQt6和PyQt6-tools。使用如下命令: pip install PyQt6 PyQt6-tools 但是运行后会报如下错误: 这个时候按照提示执行命令升级pip即可 python.exe -m pip install --upgrade pip 配置pycharm: 打开pycharm,进入setting&am…

STM32 ESP8266 物联网智能温室大棚 (附源码 PCB 原理图 设计文档)

资料下载: https://download.csdn.net/download/vvoennvv/88680924 一、概述 本系统以STM32F103C8T6单片机为主控芯片,采用相关传感器构建系统硬件电路。其中使用DHT11温湿度传感器对温度和湿度的采集,MQ-7一氧化碳传感器检测CO浓度,GP2Y101…

SpringCloud微服务架构,适合接私(附源码)

一个由商业级项目升级优化而来的微服务架构,采用SpringBoot 2.7 、SpringCloud 等核心技术构建,提供基于React和Vue的两个前端框架用于快速搭建企业级的SaaS多租户微服务平台。 架构图 项目介绍 用户权益 仅允许免费用于学习、毕设、公司项目、私活等。…

软件测试之---测试设计方法

📢专注于分享软件测试干货内容,欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!📢交流讨论:欢迎加入我们一起学习!📢资源分享:耗时200小时精选的「软件测试」资…

studio3T mongodb 根据查询条件去更新集合

mongodb 等于、不等于$ne、不包含 $nin 以及批量更新数据的使用。 业务场景: 在集合中,根据查询条件,更新数据状态。 实现代码: 1. 部门名称为XXX、状态不等于“完好”的、并且不包含这些编码的数据先查询出来2. 再把状态更新成…

STM32入门教程-2023版【3-2】推挽输出和开漏输出驱动问题

关注 点赞 不错过精彩内容 大家好,我是硬核王同学,最近在做免费的嵌入式知识分享,帮助对嵌入式感兴趣的同学学习嵌入式、做项目、找工作! 二、正式点亮一个LED灯 (4)推挽输出和开漏输出驱动问题 把LED的正负极对换&…

Sharding Sphere 教程 简介

一 文档简介 1.1 分库分表诞生的前景 随着系统用户运行时间还有用户数量越来越多,整个数据库某些表的体积急剧上升,导致CRUD的时候性能严重下降,还容易造成系统假死。 这时候系统都会做一些基本的优化,比如加索引…

Linux之下载安装

rpm包管理 rpm介绍 rpm用于互联网下载包的打包及安装工具,他包含在某些linux分发版本中。他生成具有.rpm扩展名的文件。RPM是RedHat Package Manager(RedHat软件包管理工具)的缩写,类似windows的steup.exe。 rpm包的查询指令 查询已经安装…

MFC综合实验二学习记录

文章目录 虚函数和纯虚函数的区别?MFC中什么是UPDATE_COMMAND_UI 消息如何查看控件对应的成员变量模态对话框的理解HGDIOBJ" 类型的值不能用于初始化 "CBrush *" 类型的实体错误MFC编程中CDC类型和HDC类型有什么区别?关于WIDING和ALTERNA…

深挖小白必会指针笔试题<一>

目录 引言 关键解决办法: 学会画图确定指向关系 例题一: 画图分析: 例题二: 画图分析: 例题三: 注:%x是按十六进制打印 画图分析: 例题四: 画图分析&…