Golang学习笔记_02——函数
Golang学习笔记_03——匿名函数和闭包
Golang学习笔记_04——递归函数
延迟调用
在Go语言中,延迟调用(defer)是一个非常重要的特性,它允许你安排一个函数在当前函数执行完毕后被执行,无论是否发生panic。
这通常用于资源清理、文件关闭、解锁互斥锁等操作。
1. 延迟调用
defer
语句会将一个函数推迟到包含该defer
语句的函数即将返回时执行。推迟执行的函数其参数会立即求值,但函数本身会延迟到外围函数返回前才执行。
1.1 使用场景
- 资源清理:如关闭文件、网络连接等。
- 解锁互斥锁:在访问共享资源后,确保互斥锁被释放。
- 打印调试信息:在函数结束时打印一些调试信息或日志。
1.2 示例
func DeferDemo() {
num := 100
num += 200
// 延迟调用
// 但是num的值是该函数调用位置计算的
defer printMessage(num)
num += 5
fmt.Println(num)
}
func printMessage(num int) {
fmt.Println("printMessage func")
fmt.Println("i am in defer: ", num)
}
测试方法
func TestDeferDemo(t *testing.T) {
DeferDemo()
}
输出结果
=== RUN TestDeferDemo
305
printMessage func
i am in defer: 300
--- PASS: TestDeferDemo (0.00s)
PASS
2. panic
panic
是一个内置函数,当程序运行时遇到无法恢复的错误时,可以调用panic
函数。
它会立即中断当前函数的执行,并开始逐层向上执行已注册的延迟调用(defer
语句)。
在延迟调用执行完毕后,程序会崩溃并打印出传递给panic
函数的值。
2.1 使用场景
panic
通常用于处理严重的运行时错误,如数组越界、空指针引用等。
2.2 示例
func panicDemo() {
divide_func := func(a, b int) int {
if b == 0 {
panic("divide by zero")
}
return a / b
}
defer printMessage(1)
divide_func(1, 0)
fmt.Println("不会被执行")
}
测试方法
func Test_panicDemo(t *testing.T) {
panicDemo()
}
输出结果
=== RUN Test_panicDemo
printMessage func
i am in defer: 1
--- FAIL: Test_panicDemo (0.00s)
panic: divide by zero [recovered]
panic: divide by zero
goroutine 3 [running]:
testing.tRunner.func1.2({0x1042b9dc0, 0x1042e3860})
……
3. recover
recover
是一个内置函数,它用于从panic
中恢复。
recover
只有在延迟调用的函数中调用时才有用。
在正常的执行流程中调用recover
会返回nil
,并且不会有任何效果。
如果在延迟调用的函数中调用了recover
,并且其所在的函数是由于panic
而正在退出,那么recover
会捕获到传递给panic
的值,并且阻止程序的崩溃。此时,程序会继续执行从panic
点之后的代码(如果有的话)。
3.1 使用场景
recover
通常用于在可能引发panic
的代码块周围添加额外的错误处理逻辑,以允许程序在发生严重错误时优雅地恢复。
3.2 示例
func recoverDemo() {
divideFunc := func(a, b int) int {
if b == 0 {
panic("divide by zero")
}
return a / b
}
saveDivideFunc := func() (int, error) {
defer func() {
// 捕获任何可能的panic
if r := recover(); r != nil {
fmt.Println("Recovered from panic:", r)
}
}()
// 尝试执行可能引发panic的操作
result := divideFunc(10, 0)
// 如果没有panic发生,则返回结果
return result, nil
}
divideRes, err := saveDivideFunc()
if err != nil {
fmt.Println(err)
} else {
fmt.Println(divideRes)
}
}
测试方法
func Test_recoverDemo(t *testing.T) {
recoverDemo()
}
输出结果
=== RUN Test_recoverDemo
Recovered from panic: divide by zero
0
--- PASS: Test_recoverDemo (0.00s)
PASS
源码
// defer_demo.go 文件
package function_demo
import "fmt"
func DeferDemo() {
num := 100
num += 200
// 延迟调用
// 但是num的值是该函数调用位置计算的
defer printMessage(num)
num += 5
fmt.Println(num)
}
func printMessage(num int) {
fmt.Println("printMessage func")
fmt.Println("i am in defer: ", num)
}
func panicDemo() {
divideFunc := func(a, b int) int {
if b == 0 {
panic("divide by zero")
}
return a / b
}
defer printMessage(1)
divideFunc(1, 0)
fmt.Println("不会被执行")
}
func recoverDemo() {
divideFunc := func(a, b int) int {
if b == 0 {
panic("divide by zero")
}
return a / b
}
saveDivideFunc := func() (int, error) {
defer func() {
// 捕获任何可能的panic
if r := recover(); r != nil {
fmt.Println("Recovered from panic:", r)
}
}()
// 尝试执行可能引发panic的操作
result := divideFunc(10, 0)
// 如果没有panic发生,则返回结果
return result, nil
}
divideRes, err := saveDivideFunc()
if err != nil {
fmt.Println(err)
} else {
fmt.Println(divideRes)
}
}
package function_demo
import "testing"
func TestDeferDemo(t *testing.T) {
DeferDemo()
}
func Test_panicDemo(t *testing.T) {
panicDemo()
}
func Test_recoverDemo(t *testing.T) {
recoverDemo()
}