You Cannot Simply Kill A Goroutine • The Applied Go Weekly Newsletter 2025-02-16
Your weekly source of Go news, tips, and projects
You cannot simply kill a goroutine
Hi ,
If you ever worked with system threads or thread libraries of various languages before, didn't you find the amount of ceremony around managing threads a bit exaggerated? Create, run, suspend, detach, resume,...
Or do goroutines feel odd due to the complete lack of "remote control"?
Especially, when you were new to Go, did it seem weird that goroutines cannot be stopped from the outside?
If you miss the power of thread control, this issue's Spotlight shows you how to reclaim some of it.
Keep go-ing!
– Christoph
Featured articles
We Replaced Our React Frontend with Go and WebAssembly - Dagger
Why would anyone write their frontend with Go and WebAssembly? Dagger displays operations via two real-time visualization interfaces (web and terminal), and wanted to unify the two interface codebases, which meant to kick out TypeScript and React and bring Go to the browser using the Go-App framework.
Practical OpenAPI in Go - by Alex Pliutau
From an API spec to server code, client code, and documentation.
Go 1.24.0 is released
Go 1.24 is now officially released. Have fun!
Podcast corner
go podcast() 050: Security, devops, testing in Go with Jakub Jarosz
Jakub Jarosz is writing a Go book: "Performance Tuning and Benchmarking". In this episode, he talks to Dominc St-Pierre about his transition from Python to Go, the growing importance of security in software development, the challenges of adapting legacy applications to modern cloud-native environments like Kubernetes, and the need for continuous testing and refactoring.
Cup o' Go: 💯 Go 1.24 release party - LIVE SHOW FOR EPISODE 100! 🎙️
Jonathan and Shay love to dance at two parties simultaneously. Or better, join two parties into one. In this episode, which was recorded live, they celebrate Go 1.24 and the 100th episode of Cup o' Go. Congratulations!
Spotlight: Killing me softly, or: how goroutines want to exit
If you have worked with POSIX threads in the past, or with threading libraries in various languages, you might have been surprised by the completely different approach to managing concurrency.
POSIX threads, or system threads, come with a big dashboard full of control knobs, or rather, a set of control functions such as pthread_atfork(3)
, pthread_cancel(3)
, pthread_cleanup_push(3)
, pthread_create(3)
, pthread_detach(3)
, pthread_equal(3)
, pthread_exit(3)
, pthread_kill(3)
, pthread_setcancelstate(3)
, pthread_setcanceltype(3)
, pthread_setspecific(3)
, pthread_sigmask(3)
, pthread_sigqueue(3)
, or pthread_testcancel(3)
.
Goroutines, on the other hand, have only one "knob":
The go
directive.
No create, cancel, cancel states, detach, fork handling, thread variable creation magic, or kill mechanism.
Especially, no kill mechanism.
Which raises one or another eyebrow along with the question, "but how can I stop a goroutine from the outside?"
How to stop a goroutine from the outside
Short answer: you can't.
Long answer: You can't, and you shouldn't, and you can instead politely ask the goroutine to exit.
You can't, because there simply is no killswitch.
You shouldn't, as killing concurrently executing code from the outside can have unwanted side effects on open resources or other parts of the program that are in exchange with that concurrent code.
How to ask a goroutine to exit
However, you can politely ask the goroutine to exit, and you even have several easy ways to do that:
- By closing a channel that the goroutine is listening to.
- By sending a value to a channel that the goroutine is listening to.
- By passing a
context.Context
to the goroutine and callingcontext.CancelFunc
from the outside.
Exiting through channel communication
The first two options are suitable for simple scenarios, especially if you have no Context
to pass around.
Here is how to have a goroutine exit by closing a channel:
package main
import (
"fmt"
"time"
)
func main() {
quit := make(chan struct{})
go func() {
for {
select {
case <-quit:
fmt.Println("goroutine exiting")
return
default:
fmt.Println("goroutine working")
time.Sleep(1 * time.Second)
}
}
}()
time.Sleep(3 * time.Second)
fmt.Println("asking to exit")
close(quit)
time.Sleep(1 * time.Second)
}
The case <-quit
case will be selected when the quit
channel is closed, because once a channel is closed, the readers start recieving the zero value of the channel's type immediately.
Note that it's the goroutine's responsibility to exit when the request arrives. Within the <- quit
case, the goroutine can do necessary cleanup such as closing resources or returning them to a resource pool.
The second option is similar, but instead of closing the channel, you send a value to it:
quit <- struct{}{}
(Put this in place of close(quit)
above.)
Exiting through context.Context
The third option is more flexible and is the standard way to exit goroutines that use a Context
.
A quick refresher: context.Context
is an interface that carries deadlines, cancellation signals, and request-scoped values across API boundaries and between processes.
To exit a goroutine using a Context
, you create a WithCancel
context that has a Done
channel and provides a cancel function. By calling the cancel function, you signal the goroutine to exit.
Here's an example:
func main() {
ctx, cancel := context.WithCancel(context.Background())
go func() {
for {
select {
case <-ctx.Done():
fmt.Println("goroutine exiting")
return
default:
fmt.Println("goroutine working")
time.Sleep(1 * time.Second)
}
}
}()
time.Sleep(3 * time.Second)
fmt.Println("cancelling")
cancel()
time.Sleep(1 * time.Second)
}
Goroutines are a simple concurrency mechanism
Goroutines are ideally suited for one-time concurrent work. They are extremely lightweight and have a low starting overhead. There is no need to "remote-control" goroutines to start, stop, pause, or resume them. If the work you want to implement concurrently is so complex that you need such mechanisms, forking or calling a new process is probably an approach worth considering. Or you simplify the concurrent work enough so that you won't need any sophisticated goroutine runtime management.
Quote of the Week: On writing security-critical code
The first rule of writing security-critical systems in C: Don't write security-critical systems in C.
–Me, after I came across this NASA list of 10 rules for safety-critical software development
More articles, videos, talks
OpenTelemetry: A Guide to Observability with Go | Blog | Luca Cavallin
Where to send your Go apps' logs, traces, and metrics to? Let OpenTelemetry do this for you.
Five More Features of Go 1 24 - YouTube
Featuring directory-limited filesystem access, improved finaliszers, TextAppender and BinaryAppender interfaces, GOAUTH Environment variable, and go/types iterator functions.
Go 1.24's omitzero
is another one of the best additions to the ecosystem in years · Jamie Tanna | Software Engineer
In JSON, undefined variables are simply omitted. Go has no such thing as undefined variables, but sending the zero value instead is often not an option. In Go 1.24, the struct field tag omitzero
excludes a field from the generated JSON if it has the zero value.
Go 1.24 Just RELEASED – Weak Pointers, Generic Aliases & More! (HUGE UPDATE) - YouTube
All the new and shiny things in Go 1.24.
Parallel Streaming Pattern in Go: How to Scan Large S3 or GCS Buckets Significantly Faster - Viktor Nikolaiev's blog
How to speed up traversing S3 buckets? If you know the bucket structure, you can traverse the buckets concurrently.
Projects
Libraries
GitHub - ConduitIO/evolviconf: Library for parsing versioned configurations
Versioned configration is more than just "I add my config files to Git." Applications can use versions config files to apply only the changes or roll back to a prior version.
GitHub - eaxis/levenshtein: An early-exit Levenshtein implementation aka Exitshtein
Levenshtein is an algorithm for measuring the difference between two strings. The early exit option allows comparing large texts more efficiently.
GitHub - HugoSmits86/nativewebp: Native webp encoder for Go
This webp encoder produces smaller files and is faster than Go's PNG encoder.
Tools and applications
GitHub - biswaone/go-paste-it: Create shareable snippets of text
What does a Pythonista do to get a feeling for Go? Write a full-stack app.
GitHub - Achno/gowall: A tool to convert a Wallpaper's color scheme / palette, image to pixel art, color palette extraction, image upsacling with Adversarial Networks and more image processing features
So your desktop wallpaper became boring or does not match your color theme? Gowall to the rescue.
GitHub - titpetric/etl: Convenience tooling to perform database interactions from CI, bash scripts and automation processes.
A database-agnostic CLI tool for creating and updating records in PostgreSQL, SQLite, or MySql databases.
GitHub - gost-dom/project-harmony: An example app for gost-dom
Project Harmony is an example app showcasing the use of gost-dom
, a headless browser written in Go and JS.
GitHub - serversfordev/deploy
Deployment tooling for minimalists (and a "first OSS project" project).
Completely unrelated to Go
How to "see" Wifi signals
Bees can see ultraviolet, humans can't. But humans can extend their vision through tech. A system using an array of Wifi antennas can detect the direction of Wifi radiation and visualize it as it it was a light source. (Project web site here.)
Local LLM code assistant for VIM (Apple Silico ("M" processor series) only 😢)
Use the power of AI coding assistance with Vim but without sending your data to Evil Corp's LLMs. Slight disadvantage: VimLM requires a Mac with an M-series processor.
It’s a knowledge problem! Or is it?
Why do intelligent people eat junk food? Why do software developers check in bad code? It's not because they wouldn't know better...

Happy coding! ʕ◔ϖ◔ʔ
Questions or feedback? Drop me a line. I'd love to hear from you.
Best from Munich, Christoph
Not a subscriber yet?
If you read this newsletter issue online, or if someone forwarded the newsletter to you, subscribe for regular updates to get every new issue earlier than the online version, and more reliable than an occasional forwarding.
Find the subscription form at the end of this page.
How I can help
If you're looking for more useful content around Go, here are some ways I can help you become a better Gopher (or a Gopher at all):
On AppliedGo.net, I blog about Go projects, algorithms and data structures in Go, and other fun stuff.
Or visit the AppliedGo.com blog and learn about language specifics, Go updates, and programming-related stuff.
My AppliedGo YouTube channel hosts quick tip and crash course videos that help you get more productive and creative with Go.
Enroll in my Go course for developers that stands out for its intense use of animated graphics for explaining abstract concepts in an intuitive way. Numerous short and concise lectures allow you to schedule your learning flow as you like.
Christoph Berger IT Products and Services
Dachauer Straße 29
Bergkirchen
Germany