"Update 2: If you’re coming to this blog post from a compendium titled “Go is not good,” I want to make it clear that I am ashamed to be on such a list. Go is absolutely the least worst programming language I’ve ever used. At the time I wrote this, I wanted to curb a trend I was seeing, namely, overuse of one of the more warty parts of Go. I still think channels could be much better, but overall, Go is wonderful. It’s like if your favorite toolbox had this in it; the tool can have uses (even if it could have had more uses), and it can still be your favorite toolbox!"
A somewhat click-baity title, but definitely worth a read. It does make a good point that though channels are the thing everyone has heard of Go using, the simpler mutex can be very useful. The docs for the sync package don't make it sound very friendly, but it is actually easier to understand for many simple use cases of just protecting access to a struct or map across goroutines. They make it sound in the go docs like you should never be using a mutex:
Package sync provides basic synchronization primitives such as mutual exclusion locks. Other than the Once and WaitGroup types, most are intended for use by low-level library routines. Higher-level synchronization is better done via channels and communication.