惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园 - 司徒正美
大猫的无限游戏
大猫的无限游戏
腾讯CDC
J
Java Code Geeks
博客园 - 【当耐特】
Microsoft Azure Blog
Microsoft Azure Blog
V
Visual Studio Blog
人人都是产品经理
人人都是产品经理
博客园 - Franky
博客园 - 聂微东
阮一峰的网络日志
阮一峰的网络日志
美团技术团队
云风的 BLOG
云风的 BLOG
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
U
Unit 42
雷峰网
雷峰网
B
Blog RSS Feed
博客园_首页
量子位
F
Fortinet All Blogs
罗磊的独立博客
H
Hackread – Cybersecurity News, Data Breaches, AI and More
酷 壳 – CoolShell
酷 壳 – CoolShell
C
Check Point Blog

Dizzy zone

Pangolin Private Resources With Domain Https About Redis is fast - I'll cache in Postgres n8n and large files Malicious Node install script on Google search Wrapping Go errors with caller info BLAKE2b performance on Apple Silicon State of my Homelab 2025 My homelabs power consumption On Umami ML for related posts on Hugo Probabilistic Early Expiration in Go SQLC & dynamic queries Enums in Go Streaming Netdata metrics from TrueNAS SCALE SQL string constant gotcha Moving from Jenkins to Drone My new server: MSI Cubi 3 Silent My thoughts on Ansible® Profiling gin with pprof How I host this blog, CI and tooling Refactoring Go switch statements OAuth with Gin and Goth I made my own commenting server. Here's why. Why I hate OpenApi(swagger) IDE for GO Jenkins on raspberry pi 3 How I started my professional career Kestrel vs Gin vs Iris vs Express vs Fasthttp on EC2 nano Self-hosted disqus alternative for 5$ a month
Go's defer statement
Vik · 2018-01-19 · via Dizzy zone

Defer is the golang’s version of the more familliar finally statement of the try/catch block in languages like Java and C#. The defer allows you to perform actions when surrounding function returns or panics. Unlike finally blocks though it does not need to be placed at the bottom of the code block. You can also have multiple defer statements in a single function body. This allows for handy clean up of resources. It does have its downsides though. It does add overhead so using it everywhere might not be the best idea.

A couple of examples

My favorite use of defer statements is in coalition with sync.WaitGroup. Wait group allows you to start multiple goroutines and wait until they are finished. I use defer wg.Done() as a defer statement to let the wait group know that the routine has done its job.

func main() {
	defer fmt.Println("All routines are done!")
	var wg sync.WaitGroup
	wg.Add(3)

	for i := 0; i < 3; i++ {
		go func(j int) {
			defer wg.Done()
			fmt.Println(j)	
		}(i)
	}
	wg.Wait()
}

Try it on go playground

Another use case is cleaning up resources, such as making sure to close readers. Here’s an example:

res, err := client.Do(req)
if err != nil {
 // error handling goes here...
}
defer res.Body.Close()
// do something with body

Function execution timer with defer

Ocassionally I’ll want to log the execution times of different functions. This can be done through a simple timer package.

type Timer struct {
	start time.Time
	defaultValue string
}

func New() *Timer {
	return &Timer{
		start: time.Now()
    }
}

func (t *Timer) End() {
	fmt.Printf("Execution took %v", time.Since(t.start))
}

If you put it into a timer package, you can then use it on any function like this:

func main() {
    defer timer.New().Done()
}

For extra flavor, we can improve the log message to include the caller name of the function we are profiling. All we need to do is write a function like this:

func (t *Timer) GetCallerName() (name *string) {
	uintpt := make([]uintptr, 1)
	n := runtime.Callers(3, uintpt)
	if n == 0 {
		return
	}
	fun := runtime.FuncForPC(uintpt[0]-1)
	if fun == nil {
		return
	}
	nameTemp := fun.Name()
	name = &nameTemp
	return
}

And change the End() function like so:

func (t *Timer) End() {
	callerName := t.GetCallerName()
	if callerName == nil {
		callerName = &t.defaultValue
	}
	fmt.Printf("Execution of %v took %v", *callerName, time.Since(t.start))
}

Here’s a full gist of the code I ended up with.

Defer performance hit

As mentioned before, there’s a performance hit for using defer but as the benchmarks I linked to before are rather old, I made one myself. I’m not an expert on benchmarking, so please let me know if anything’s not right. Here’s the result:

Method ns/op
No defer 98986
With defer 138555

There’s a hit, but I would not worry if you’re using defer in a reasonable manner and the performance difference of a few nanoseconds does not impact you that much. I really do prefer Go’s approach to the more standard finally block.

What about you? How do you use defer? Let me know in the comments below!