Hi Drew,
In your post on Go 1.11 back in 2018 [1], you speak highly of Go's
concurrency model, but in your most recent post [2] you state that Go's
concurrency/parallelism model is one that you "strongly dislike". I'm
curious to hear your thoughts on what changed your opinion here.
In fact, since I know you are a big fan of C and also _not_ a fan of
threads, I would love to hear your thoughts on concurrency in general
(e.g. what makes up a good model, real world examples of concurrency you
like, how to achieve concurrency in C, etc.)
Regards,
Greg
---
[1]: https://drewdevault.com/2018/10/08/Go-1.11.html
[2]: https://drewdevault.com/2020/01/08/Re-Slow.html
Hi Greg! My opinion of the concurrency model has changed since then. I
initially viewed it as a black box, and I had heard lots of high-level
ideas around its design. Since the Go 1.11 post, I have opened the black
box, and my opinion has changed.
First, the runtime works by inserting cooperative context switching
opportunities throughout your code. This makes the mapping of Go code to
machine code much, much less obvious, as there are mysterious
checkpoints inserted throughout your program.
Additionally, Go posits that you don't have to worry about cooperative
vs preemptive multithreading - it'll magically schedule your tasks as
either and adjust them as necessary! This is a big, big idea, and it
seems exciting on the surface. But you have to remmeber that the
problems of preemptive switching are MUCH more complex than the problems
of cooperative switching. When your task can magically switch from being
cooperatively scheduled to preemptively scheduled, this means that all
code has to be prepared to deal with the consequences of preemptive
scheduling, which accordingly means all code has to be much more
complex.
This runtime design leaks into the stdlib design as well. The
appropriate way to multiplex input sources in Go is by spawning several
Goroutines to do blocking reads. The Go runtime will magically turn
these into polls, maybe. Or it could magically spawn threads. You can't
plan for this, and the runtime behavior of your software is
unpredictable. And there's no other way to process input events - you
couldn't opt-out of preemptive switching in favor of a traditional
poll-driven event loop, for example.
I _do_ still really like Go, and I write Go programs often. But the
runtime is back-asswards and I try to avoid using the
concurrency/paralleism (concuparallelicy?) tools it offers. I just seem
them as a massive footgun now. The runtime makes Go a much more
high-level programming language than it otherwise might be, rendering it
useless for many systems programming tasks.