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

推荐订阅源

美团技术团队
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Martin Fowler
Martin Fowler
雷峰网
雷峰网
IT之家
IT之家
小众软件
小众软件
M
MIT News - Artificial intelligence
博客园 - 聂微东
J
Java Code Geeks
Blog — PlanetScale
Blog — PlanetScale
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
A
About on SuperTechFans
G
Google Developers Blog
Engineering at Meta
Engineering at Meta
Recent Announcements
Recent Announcements
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
The GitHub Blog
The GitHub Blog
F
Fortinet All Blogs
C
Check Point Blog
云风的 BLOG
云风的 BLOG
腾讯CDC
H
Help Net Security
Y
Y Combinator Blog
I
InfoQ

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!