When they announced generics the first thing I did with it was rewrite my common slice parallel algorithm and my limited concurrency pool. It is an obvious area needing improvement for common use cases.
That’s because people confuse “easy” and “good”.
Go has easy concurrency, but it’s not good. The opposite really. It’s at best the similar problematic model you get in other, older, procedural / oo langages. At worst it’s a lot worse given how difficult it is (and even more so was before generics) to build abstractions and concurrent data structures.
That nothing about it’s good? It’s the same shared-memory concurrency everybody else has, that it had to built-in a thread-safe queue doesn’t fix that.
In some ways it makes it worse, because it’s easy for the unwary to assume channel = safe, whereas nothing could be further from the truth: a channel only protects itself, sending a slice through a channel is not safe, sending a map through a channel is not safe, sending a pointer to a channel is not safe, etc… and this is transitive, sending by value a struct which contains any of those isn’t safe either.
Not to mention the easy and common issue of implicit concurrent sharing via `go` closures.
Here’s a bunch of examples: https://www.uber.com/blog/data-race-patterns-in-go
And these are data races, not merely concurrency issues.
You have a very myopic view of what makes concurrency designs good.
I suggest you broaden your perspective, and consider the practical effects of language design on humans, as opposed to just mathematically provable safety.
Read the Structured Concurrency notes (https://vorpus.org/blog/notes-on-structured-concurrency-or-g...) for the reasons why.
The only reason why people don’t see the similarities when it comes to in-process concurrency is that the request-response paradigm muddies the waters. Anyway, that’s the world we live, so people have to work around the N+1 problem and similar, living in a mixed sync-async world of pain :)
It’ll take at least a decade to fix, if we ever do.
There is a non-negligible number of problems that don't need multiple machines and are much easier to deal with using other concurrency paradigms. When the language forces only one model onto the developer, things are actually more complex than they need to be.
My main criticism against the paper is that what tipped the scales for concurrency bugs is the “blocking category” which of course is going to be higher with message passing since it’s a blocking paradigm. Locks only block until unlocked, and isn’t used as a general purpose signaling mechanism.
The “non-blocking” bugs are over represented by locks, which are generally more serious (non-deterministic data races vs deterministic deadlocks).
That said, I have criticism against Go as well. The decision to not implement structured concurrency (fire-and-forget goroutines) makes these deadlocks largely go unnoticed. If goroutines had ownership or scope, runtime detection a la ‘-race’ would help detect a lot of these bugs.