Your comment is spot on though: I'm only aware of two modern implementations that really work : Erlang and Go.
What can be slow are context switches, but they aren't slow in absolute terms. The vast majority of applications, including Web servers, are perfectly fine with 1:1 threading.
Also, if nothing else, you'll run out of PID numbers, as they're usually still 16 bits, even today, though there was a kernel compilation option to change that, from what I remember.
/proc/sys/kernel/pid_max
> PID_MAX_LIMIT, approximately 4 million
/proc/sys/kernel/threads-max
> FUTEX_TID_MASK (0x3fffffff), [approximately 1 billion]
For per-process limit, increase RLIMIT_NPROC.
4K for kernel stacks, only 2K with future work. That's really not much space at all if you're doing anything interesting with those threads.
> Also, if nothing else, you'll run out of PID numbers, as they're usually still 16 bits, even today
Not for a very long time.
yup they do.
till you start making sure that your code doesn't end up with deadlock, data-corruption, races, performance issues due to lock-contention etc. etc. designing efficient locking schemes is notoriously hard alternating between:
- too coarse grained : resulting in serializing activities which could have (should have) proceeded in parallel, thereby sacrificing performance and scalability.
or
- too fine grained: with space+time for lock operations sapping performance, error recovery and not to mention understanding etc. etc.
In the former we have the dragons of deadlock and livelock roaming freely, and in the latter we have race conditions. Somewhere in between is a razor's edge which is both efficient and correct.
Almost always, things start with ‘one big lock around everything’ and the vague hope that performance might not be abysmal. When that is dashed, big lock gets broken up, and the prayer is repeated. Each iteration increasing complexity and decreasing lock-contention, and hopefully with some luck, modest performance gain as well.
remember this:
What do we want ?
Now !
When do we want it ?
Fewer race conditions !
have fun :)
Of course a better solution would have been to fix the kernel rather than go back to 1980s cooperative multitasking in userland.
Which APIs are you seeing that are async and seem like they shouldn't be?
Promises make code more legible than callback chains, and async/await make code more legible than promise chains. Do you have any examples of buggy/illegible code and the more legible less buggy alternative?
Depending on what framework/runtime you're in .NET will schedule the await continuation on something called a "SynchronizationContext" which has ~3 different forms but it's basically an event loop/message loop which queues up each continuation on the original thread.
The problem occurs when you use .Wait() or .Result instead of 'await'. This causes the function to spin waiting for the Task to finish, which of course it never will if it has a continuation trying to dispatch into that same event loop.
This problem doesn't really happen at all under some circumstances, such as if the async chain starts on a background thread, or in .net core where they've removed the SynchronizationContext, hence no event loop, hence no problems.