1 day ago
Thurs Feb 20, 2025 7:55pm PST
Ask HN: event loops vs. greenthreading in modern languages
Most explanations I've seen on "async programming" bury the reason why we're doing this in the first place. It's purely to avoid making OS threads wait on I/O in a concurrent application, such as a webserver. So languages or even libraries instead have various ways to switch tasks on a single thread. If OS threads were cheap enough to just spawn one for each request, this wouldn't be a thing.

Some languages with built-in event loops have async/await syntax, like JS, C#(?), and now Python and Rust. This is convenient syntax. Java, ObjC, etc also had a lot of futures-based stuff that mangled up your code somewhat. Callbacks were also common in ObjC, which gave you crazy nesting. Either way, you're explicitly saying when it can switch tasks, aka what you expect to block.

Golang has greenthreading ("goroutines") instead, and Java recently got them back (as "virtual threads"), which is what I'm less sure about. I get that N greenthreads can run on 1 OS thread, and they're cheap enough to spawn whenever you want. This is less explicit than the async/await way, because the runtime automatically decides what is blocking (i.e. when to switch out the greenthread running on the OS thread), which I assume is based on what syscalls it uses.

So now I'm wondering, if Golang and now Java can task-switch without the user having to tell it when, is there any point of doing it explicitly like in JS or Rust? Is it faster or something, or are other runtimes just not able to tell when exactly things are blocking?

comments:
add comment
loading comments...