Hacker News new | ask | show | jobs
by giovannibajo1 3151 days ago
That is exactly what Go does by default. Any time a blocking operation is performed, Go either leaves the OS-level thread blocked there and switches away, or hand the blocking operation to an internal thread which is running epoll for the whole process.

The end result is much easier than Python/NodeJs because there is no explicit "async/await" or deferred-style programming. You simply write linear code and any blocking operation (at the syscall level) is transparently handled.

2 comments

Thanks for taking the time to write this response. The more I hear about Go's features, the more I seem to like it.
FYI, this is not unique to Go. A number of languages implement lightweight threads in the same way (roughly), like erlang or Haskell. It’s all epoll or other efficient polling primitives under the hood.
I'm of the impression that there's an important difference between Go and Haskell's models--namely that Go is M:N threaded and Haskell is not; however, I don't entirely understand the significance of the difference, so hopefully someone else can comment and enlighten me.
Are you sure about Haskell not having m:n threads? Because it's seems weird, considering GHC is listed as one of a few examples on wikipedia: https://en.m.wikipedia.org/wiki/Thread_(computing)#M:N_.28hy...
No, I'm not sure. :) I may have incorrectly assumed that the definition of M:N threads includes movable application threads (e.g., Go's scheduler can move goroutines from one kernel thread to another).
In other words, Go uses threads.

There's nothing semantically unique to Go about this model.

It depends if you're describing a semantic model or you're concerned about implementation details.

Semantically, a goroutine is a thread, within a shared memory model. But what makes Go unique (or let's say more unique) is that it offers programmers a thread-like programming approach (linear, blocking code) but internally turns it into an event-driven approach (epoll/kqueue) for networking.

Moreover, the fact that goroutines are much cheaper than OS-level threads enable a more pervasive approach to concurrency.

Go uses an m:n thread model. Goroutines are multiplexed onto a smaller number of os-level threads. They're sort of like threads, but they have a simplified programming model (There is no thread-level storage for example).
Goroutines are threads. They're just not kernel-level threads.

There have been implementations of pthreads that used an M:N model, for instance.

For my information, why is M:N so successful for Go and not for pthreads? Is M:N more practical for systems with a particular kind of garbage collector?
In my opinion, it's because the Go team put a ton of effort into getting M:N working and didn't rigorously evaluate any other alternatives in Go, once moving GC was implemented.

I'm not convinced that 1:1 wouldn't have been a perfectly reasonable implementation strategy for Go.

This is surprising. It seems like Go gets pretty high praise from all over for its threading model, and it seems like there are relatively few high-performance servers that are built with 1:1. Am I wrong about that? If not, what explains this?