3 ms·
It's also very easy to implement a future/promise style API over a blocking IO primitive, as long as you have cheap threads: you spawn a thread that executes th
by tsimionescu 2y ago
It's also very easy to implement a future/promise style API over a blocking IO primitive, as long as you have cheap threads: you spawn a thread that executes the blocking operation (with cancelation and timeout support as needed) and sets the future's result once the result is done, or some error state. It's really not such a huge problem.
I will also note that most async runtimes include much more complex program rewrites and implicit state machines than thread based models. Java style or Go style green threads are much simpler frameworks than C#'s whole async task machinery, or even than Rust's Tokio.
And any program written as a series of threads running blocking operations with proper synchronization is also pretty easy to convert to an async model. The difficulty is taking a single-threaded program and making it run in an async model, but that is a completely different discussion.
However, I do agree that ultimately you do need the OS to provide async IO primitives to have efficient IO at the application level. Since OS threads can't scale to the required level, even the green threads + blocking IO approach is only realistically implementable with async IO from the OS level. This could change if the OS actually implemented a green threads runtime for blocking operations, but that might still have other inefficiencies related to costs of crossing security boundaries.
- Veserv 2y agoYes, if you have cheap threads at the required scale. But that is the entire problem as you also attest and agree that with the author and me that OS threads do not scale to the required level. Asynchronous, non-blocking primitives do scale to the required level, demonstrate greater hardware sympathy, and can easily and practically be used for the other half of the equation, blocking I/O, at the required scale. Asynchronous primitives robustly solve the entire problem space, where as synchronous primitives suffer in high concurrency cases. The only reason to prefer only exposing/implementing synchronous primitives in the API is due to implementation complexity. But at the OS layer you are abstracting hardware interfaces that almost always present asynchronous interfaces. Thus, exposing asynchronous APIs is usually not very hard where as exposing synchronous APIs is actually a mismatch that requires smoothing over (though to be fair not very much since, as I mentioned previously, implementing a synchronous operation in terms of asynchronous primitives is quite easy). Though in truth I think we largely agree anyways. I was just presenting a more complete explanation.