C# and Rust (via Tokio) both have M:N threading. They both use a work-stealing algorithm to map many tasks onto a finite thread pool. But you're correct that they are cooperative via async/await, not pre-emptive.
C# and Rust (via Tokio) both have M:N threading. They both use a work-stealing algorithm to map many tasks onto a finite thread pool. But you're correct that they are cooperative via async/await, not pre-emptive.
I feel like you can't describe something as m:n when it the m uses async/await and doesn't actually have a green thread?
Is it really that different in implementation? Go's yield points are implicit but aside from the devex side how is that different from async await?
Define the "green thread"? By every definition I've seen[1], the task objects I mention are green threads.
[1]: https://en.wikipedia.org/wiki/Green_thread
Very curious how much Go wins by this. How worse would typical Go programs run with pre-emption disabled?
You can try this yourself: GODEBUG=asyncpreemptoff=1
Also platforms like Wasm still do Mx1 scheduling without async preemption, where Gosched is required at places.
E.g.: my "transpiled" SQLite driver takes special care to make sure long running SQL queries (and the busy handler) can be canceled with contexts even on platforms without async preemption.
I believe it’s more about robustness than performance in typical cases. Without pre-emption, there’s always a risk of one goroutine using disproportionate CPU time if it gets into an infinite (or just very long) loop without doing any IO.