Go Routines

2 min read


Waiting for goroutines

Yesterday we looked at some sample code with goroutines: for i := range 10 { go func() { fmt.Println(i) }() } But if you ran it, you probably saw no output. This is because the main goroutine exits before the 10 new goroutines have a chance to run. How do we solve this? We need some sort of synchronization mechanism in place, to ensure we wait for all goroutines to finish before exiting the program.

Go Routines

3 min read


Goroutines

Today I’m starting a new series of posts on the topic of goroutines. In the past, I’ve mostly walked through existing documentation (the Go spec, docs for standard library packages, etc), and provided my commentary and explanation. Starting today, instead, I’ll be going on a topical journey through goroutines. I will, of course, touch on the spec, and some standard library packages, but I’ll be taking my own path through the topic.


Dependency pinning

Today I want to share a trick I stumbled upon. It won’t matter to most of you. If it does matter to you, it will probably save you a bunch of headaches! First, some background. Have you ever had to use an outdated dependency for $REASONS? If not, you can skip today’s email, unless you’re just curious. One of my clients is still using MongoDB 3.2. MongoDB was EOLed more than a year before anyone had ever heard the term “COVID”.


Why is context.TODO not just a comment?

I’ve been on vacation for the last week, so haven’t written much. Let’s finally finish up the context package series with some reader feedback and questions! Joost Helberg wrote in with an interesting observation about context.TODO. In his own words: With regards to context.TODO, back in my early Go days, I thought the TODO was about the intent of this context. Like context.Background is for a background task and context.WithCancel for a job that can be canceled.

Subscribe to Boldly Go: Daily

Every day I'll send you advice to improve your understanding of Go. Don't miss out! I will respect your inbox, and honor my privacy policy.

Unsure? Browse the archive.


context.WithoutCancel

This is it! The last of the context package functions that we will cover in this series. Well, almost. I actually have a couple reader feedback and question emails to respond to, which I’ll do next, before startinga a new series. Speak of a new series: I’m still looking for suggestions! I have a couple ideas, but I’d rather do something you’re itching to learn about, than whatever random idea I come up with.


context.WithValue

func WithValue func WithValue(parent Context, key, val any) Context WithValue returns a derived context that points to the parent Context. In the derived context, the value associated with key is val. Use context Values only for request-scoped data that transits processes and APIs, not for passing optional parameters to functions. The provided key must be comparable and should not be of type string or any other built-in type to avoid collisions between packages using context.


context.TODO

We’re almost done going through the context package. What would you like me to cover next? If you have a suggestion, hit reply, and let me know! – func TODO func TODO() Context TODO returns a non-nil, empty Context. Code should use context.TODO when it’s unclear which Context to use or it is not yet available (because the surrounding function has not yet been extended to accept a Context parameter).


context.Background

After this detour through context key types, let’s get back to the GoDoc for our context package. Next up: context.Background(): func Background func Background() Context Background returns a non-nil, empty Context. It is never canceled, has no values, and has no deadline. It is typically used by the main function, initialization, and tests, and as the top-level Context for incoming requests. Why would you ever want a context that essentially does nothing?


Context key/value utilities

So we’ve finally settled on a suitable type for our context key types: string, or maybe a non-empty struct. We also understand that to guard against key collisions, we need our custom type to be un-exported. type contextKey string const KeyUserID = contextKey("user_id") Now code anywhere in your project can use the context key without fear of collision. Done! Right? I’m sure you know the fact that I’m asking means there’s more we can do.


Context key type: Final recommendation

First, a correction! An astute reader pointed out that I made a small mistake in my post on June, 10, with regard to string context keys. My code example showed: type contextKey string const ( KeyUserID = "user_id" KeyTransactionID = "transaction_id" KeyMessageID = "message_id" ) But it should have been: type contextKey string const ( KeyUserID contextKey = "user_id" KeyTransactionID contextKey = "transaction_id" KeyMessageID contextKey = "message_id" ) This is a nasty kind of bug, because the code will continue to work as expected—just without any protection from key collisions!


Struct context keys

First off, an apology for being rather unreliable with my “daily” emails. I’m in the middle of some traveling (was in the UK and Netherlands last week, and will be on a US road trip starting tomorrow), so I’m writing when I have a spare moment, which isn’t that often. I’ve been talking about finding the ideal type for a context key. So far I’ve looked at empty structs (e.g. struct{}), integers, and strings.