Hacker News new | ask | show | jobs
by Nican 847 days ago
I have been out of the loop with Java. Is Virtual Threads the answer to asynchronous I/O? (Much like Go/C# async/node.js?)

That looks like an interesting solution to support asynchronous I/O without breaking all the APIs, and having the async/await mess that C# created.

1 comments

    > ...the async/await mess that C# created
What do you find messy about it? Seems fairly straight forward, IME.
Not sure what the poster above was thinking of, but it seems kinda the same as every other language that’s adopted it - powerful, but footguns abound. I ran some async C# in a debugger - in Rider - the other month, and the debugger just goes off the deep end.

Does C# have the same issue Python does with accidentally calling blocking code? In async Python - which I mostly quite like actually - it’s terrifying to bring in a library because if you’re unlucky it does some blocking network call deep inside that stalls your whole app..

.NET uses threadpool with hill-climbing algorithm. Each worker thread has its own queue and can also perform work stealing. However, if that is not enough and the work queues fill up faster than the items in them get processed, the threadpool will spawn additional threads until the processing lag is counteracted (within reason).

This historically allowed it to take a lot of punishment but also led to many legacy codebases to be really shoddily written in regards to async/await, where it would take some time for threadpool to find the optimal amount of threads to run or devs would just set a really high number of minimum threads, increasing overhead.

In .NET 6, threadpool was rewritten in C# and gained the ability to proactively detect blocked threads outside of hill-climbing algorithms and inject new threads immediately. This made it much more resilient against degenerate patterns like for loop + Task.Run(() => Thread.Sleep(n)). Naturally it is still not ideal - operating system can only manage so many threads, but it is pretty darn foolproof if not the most foolproof amongst all threadpool implementations.

As of today, it is in a really good spot and with tuned hill-climbing and thread blocking detection you would see the .NET processes have thread counts that more or less reflect the nature of the work they are doing (if you don't do any "parallel" work while using async - it will kill most threads, sometimes leaving just two).

    > Does C# have the same issue Python does with accidentally calling blocking code? 
This can't really happen in C# except maybe if you are working on a GUI thread and you make the mistake of running blocking code on a GUI thread.

For APIs, console apps, and such, it's not a concern for actual parallel-concurrent code. Of course, if you write a standard non-parallel `for` loop that has blocking code, it's going to block in a console app as well if you don't run it on a thread.

But I think that once you do enough JS/TS or C#, `async/await` doesn't feel very onerous if you have the basic concept.

Python's async works on a single thread, C# uses a thread pool. Calling a blocking method is not ideal, but doesn't ruin everything, and it's easy to hand that work off to a separate thread by using Task.Run.
Agreed. The task system in C# is pretty clean imo. Same with Rust (sans the type system implementation of Futures) and Go's goroutines. Especially compared to CompletableFuture in Java.
Specifically, function coloring (C# and Rust in your examples) is not the same as coroutines in Go or virtual threads in Java.
Sure function coloring can be a problem, but the gp just spoke about async/await being a mess.

Function coloring can be handled by just blocking on an async function. Though the reverse takes some planning.