Golang学习笔记_05——延迟调用

Golang学习笔记_02——函数
Golang学习笔记_03——匿名函数和闭包
Golang学习笔记_04——递归函数



延迟调用

在Go语言中,延迟调用(defer)是一个非常重要的特性,它允许你安排一个函数在当前函数执行完毕后被执行,无论是否发生panic。

这通常用于资源清理、文件关闭、解锁互斥锁等操作。

1. 延迟调用

defer语句会将一个函数推迟到包含该defer语句的函数即将返回时执行。推迟执行的函数其参数会立即求值,但函数本身会延迟到外围函数返回前才执行。

1.1 使用场景
  1. 资源清理:如关闭文件、网络连接等。
  2. 解锁互斥锁:在访问共享资源后,确保互斥锁被释放。
  3. 打印调试信息:在函数结束时打印一些调试信息或日志。
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()
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值