Golang实现延迟调用的项目实践

 更新时间:2025年02月17日 11:21:39   作者:LuckyLay  
本文主要介绍了Golang实现延迟调用的项目实践,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

延迟调用

在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()
}

到此这篇关于Golang实现延迟调用的项目实践的文章就介绍到这了,更多相关Golang 延迟调用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • vscode插件设置之Golang开发环境配置全过程

    vscode插件设置之Golang开发环境配置全过程

    go语言开发选择vscode作为IDE工具也是一个不错的选择,下面这篇文章主要给大家介绍了关于vscode插件设置之Golang开发环境配置的相关资料,文中通过图文介绍的非常详细,需要的朋友可以参考下
    2022-12-12
  • Go通过SJSON实现动态修改JSON

    Go通过SJSON实现动态修改JSON

    在Go语言 json 处理领域,在 json 数据处理中,读取与修改是两个核心需求,本文我们就来看看如何使用SJSON进行动态修改JSON吧,有需要的小伙伴可以了解下
    2025-03-03
  • 一文带你感受Go语言空结构体的魔力

    一文带你感受Go语言空结构体的魔力

    在 Go 语言中,有一种特殊的用法可能让许多人感到困惑,那就是空结构体,本文将对Go空结构体进行详解,准备一杯你最喜欢的饮料或茶,随着本文一探究竟吧
    2023-05-05
  • 使用go操作redis的有序集合(zset)

    使用go操作redis的有序集合(zset)

    这篇文章主要介绍了使用go操作redis的有序集合(zset),具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12
  • golang数组和切片作为参数和返回值的实现

    golang数组和切片作为参数和返回值的实现

    本文主要介绍了golang数组和切片作为参数和返回值的实现,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-02-02
  • 详解go语言中type关键词的几种使用

    详解go语言中type关键词的几种使用

    这篇文章主要介绍了详解go语言中type的几种使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-02-02
  • Golang并发之RWMutex的用法详解

    Golang并发之RWMutex的用法详解

    在 Go 语言中,RWMutex 是一种读写互斥锁的实现,它提供了一种简单有效的方式来管理对共享资源的并发访问。本文就来和大家详细聊聊RWMutex的用法吧
    2023-04-04
  • go语言数据类型之字符串string

    go语言数据类型之字符串string

    这篇文章介绍了go语言数据类型之字符串string,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-07-07
  • 详解Go语言如何使用xorm实现读取mysql

    详解Go语言如何使用xorm实现读取mysql

    xorm是go语言的常用orm之一,可以用来操作数据库。本文就来和大家聊聊Go语言如何使用xorm实现读取mysql功能,感兴趣的小伙伴可以跟随小编一起学习一下
    2022-11-11
  • Golang中的闭包(Closures)详解

    Golang中的闭包(Closures)详解

    在 Golang 中,闭包是一个引用了作用域之外的变量的函数,Golang 中的匿名函数也被称为闭包,闭包可以被认为是一种特殊类型的匿名函数,所以本文就给大家详细的介绍一下Golang的闭包到底是什么,感兴趣的小伙伴跟着小编一起来看看吧
    2023-07-07

最新评论