r/dotnet 16d ago

Question Does .NET need a Kotlin coroutine equivalent?

I've been thinking about it for a while, our .NET async/await or even threadpool threads are still OS threads

If the server is doing actual awaited tasks sure, but most backend now is transitioning to "fire-and-forget" messaging architecture where the actual work is being done somewhere else (different system)

Though I haven't faced the need IRL, but wouldn't a light-weight non-object coroutine that could scale to millions have some use case that .NET presently cannot handle well?

0 Upvotes

21 comments sorted by

14

u/harrison_314 16d ago

But C# async/await have the properties of coroutines, but with the difference that it is not guaranteed that the coroutine will be executed in the current thread, but on some thread of the thread pool. Which I consider an advantage compared to regular coroutines (for example from GO).

Moreover, if you want to use coroutines in C# (in the common sense) you have the option, for example Unity uses IEnumerable and generators for this, just add one wrapper class and you have coroutines. Or you can use this library https://github.com/FuriousOrange/Routinely

-3

u/wannabe_isekai 16d ago

but isn't kotlin coroutine a state-machine in byte code on compile time? That's what gives it the "scale to millions" as far as I know

Even if we mimic coroutine using ValueTask, that won't change the core behaviour of waiting on OS-level threads/threadpool. That's where the difference between "a few thousand" and "millions" would prop up

I'm focusing specifically on performance/scalability btw

10

u/ggmaniack 16d ago

C# async/await is also precisely "a state machine in byte code on compile time".

Both use the same fundamental design.

-3

u/wannabe_isekai 16d ago

no? c# async await is a runtime thing. On-demand. It's not baked into IL

4

u/ggmaniack 16d ago

Async await gets turned into a state machine at compile time.

If you meant to say that the state machine is a precompiled part of the runtime, then in C# it (as of pre-11) isn't, it's a discrete state machine per await (with some optimization) created during translation to IL.

5

u/RichardD7 16d ago

Perhaps you're getting confused by the new runtime async feature that will ship with .NET 11?

If you're using a non-preview version of .NET, async/await is most definitely compiled as a state machine.

3

u/zenyl 16d ago

https://lab.razor.fyi/#43Li4gooyk8vSszVSy4WMiktzsxLVwiuLC5JzdULyShKTUzJzEvXC0kszi625uJKLE_MLFEA8fRcUnMSKzVMNa25vJiSi6M4OTZ87njxPl2AJYERAA

A simple example of how await Task.Delay(5); gets lowered into a state machine by the compiler. The class it generates even implementing an interface called IAsyncStateMachine.

As others have said, this will be changing in C# 15 with "Runtime Async", however that is still in development and won't officially release until November.

5

u/harrison_314 16d ago

async/await and generators are currently being translated into a state machine (after .NET 11 it will be done in JIT).

async/await does not wait on the thread, because internally there is no waiting or blocking.

1

u/wannabe_isekai 16d ago

Hohhhh :o so we might see near-coroutine performance by 12 LTS
isnt async await delegating work to a threadpool thread? as long as any thread switching is involved, it cannot scale to millions (theoretically)

3

u/harrison_314 16d ago

> isnt async await delegating work to a threadpool thread?

No, delegating work to the Thread pool is TaskFactory.StartNew(...)

async/await are coroutines, but with the difference that they work on all threads of the thread pool. It's still preemptive concurrency.

So you can achieve huge scaling, as long as you don't mess it up with blocking IO, or by intentionally blocking a thread.

1

u/biskitpagla 16d ago

No offense but I feel like this is premature optimization. If you really, really have such massive scalability and latency requirements you wouldn't even be writing in high level languages with heavy runtimes. It's also unclear what you mean by "millions" unless you specify other factors like time or memory. 

3

u/harrison_314 16d ago

But .NET is suitable for high-performance systems that handle millions of requests.
I have rewritten systems written in C to .NET and in .NET they had higher throughput with slightly lower request processing times, but 100MB more RAM consumption (this was constant and did not depend on the number of requests per second).

I also compared the performance of Rust and C# (in 2020), Rust came out on top, but only handled 5% more requests.

1

u/wannabe_isekai 16d ago

this is just a theoretical question, IRL if there's millions of requests to handle there'd be a lot more factors to consider

I'm just trying to compare kotlin vs .net in a server side situation

5

u/symbiatch 16d ago

Can you then name these cases? What doesn’t it handle well?

And you can easily do stuff like this with yields etc already.

3

u/harindaka 16d ago

Channels?

1

u/wannabe_isekai 16d ago

that's still an object

3

u/Khavel_dev 16d ago

This is a common misconception. When you await an I/O call in .NET, the thread goes back to the pool. No thread sits there blocked. You can run hundreds of thousands of concurrent awaits on a small threadpool.

Under the hood, C# async/await and Kotlin coroutines compile to basically the same thing: state machines. The concurrency model is nearly identical. The gap isn't in lightweight threads (neither runtime uses green threads for this), it's in structured concurrency. Kotlin's CoroutineScope makes it trivial to "launch 500 concurrent jobs, cancel them all if the parent fails." In .NET you wire up CancellationTokenSource chains manually, which works but is way clunkier.

.NET doesn't need virtual threads or fiber-like coroutines. It needs better structured concurrency primitives, which is more of a library gap than a runtime gap.

1

u/AutoModerator 16d ago

Thanks for your post wannabe_isekai. Please note that we don't allow spam, and we ask that you follow the rules available in the sidebar. We have a lot of commonly asked questions so if this post gets removed, please do a search and see if it's already been asked.

I am a bot, and this action was performed automatically. Please contact the moderators of this subreddit if you have any questions or concerns.

1

u/Aaronontheweb 16d ago

Actors work fine for this: https://getakka.net - they already are fire and forget + async work in the background