Just a note :
Mutating a single word value should generally be atomic if you run the code on the same platform you compiled it on. But there is more to it that makes the use of atomic instructions compulsory (if you don't plan to use stronger guarantees even).
Reason is that besides memory reorderings at the compiler level, you might have reorderings at the CPU level for optimizations. (writes do not propagate immediately to main memory, CPU have store buffers etc).
So in a multithreaded environment, you need to explicitly make the changes visible to all cores or otherwise you will corrupt memory.
As far as mutating a composite type is concerned, yes, you need to be (VERY) careful. Easiest way is to lock the whole object (embedding a mutex is a good way to do it, it is more cache friendly).
Otherwise, if you want to do it atomically, you could adopt a Read-Copy-Update methodology (look up for RCU or Copy-on-Write) but BEWARE !! Unless you really know what you are doing you could get into trouble very very easily. It's difficult to do that sort of thing when you have mutable objects nested into other mutable objects (lookup for issues regarding linearizability of nested lock-free data structures). It is really tricky and I discourage it. Even if you were to add an extra level of indirection so that your datastructures look immutable, solving the issue of concurrent atomic reads, updates and deletes is PhD level stuff. If you are curious though, lookup for Aleksandar Prokopec's Thesis and papers : http://axel22.github.io/home/professional/
That's the reason channels and mutexes are here for you. And I expect incremental performance improvement on the already nice performance the channels have. Now channels are easy for some, but it took a few days for me to really wrap my head around them. But once done, it's really simple to use them.