3 ms·
I'm really out on most of the "async" stuff, after having used it. (Mostly in Node and Tornado) Remember in the early 90s when Windows and Mac OS were "coopera
by overgard 12y ago
I'm really out on most of the "async" stuff, after having used it. (Mostly in Node and Tornado)
Remember in the early 90s when Windows and Mac OS were "cooperatively" multitasked? Which is to say, you had to explicitly yield to allow other applications to run (or risk locking up the entire system). And then it was replaced with pre-emptive multitasking, which allowed the scheduler to figure out what process deserved CPU time, while allowing the programmer not to have to think about it. You could call a blocking IO function, and the OS would just go do something else while you waited.
All this "async" stuff seems like a return of cooperative multitasking, only worse. Not only do I have to explicitly yield, but now it's to some event loop that can't even properly use multiple cores, or keep a coherent stack trace. It's a nightmare to debug. It's theoretically fast... except if one request forgets to yield, it can clog up the entire thing. I guess you use multiple processes for that and a dispatcher, but at that point you've basically reinvented preemptive multitasking... badly.
Threads aren't perfect, but excluding STM and actor models they definitely suck the least.
- woah 12y agoHow does one request "forget to yield"?
- deathanatos 12y agoSome of these cooperatively multithreaded implementations have "green" varieties of all your standard functions; these are greenlet-aware (that is, aware of the cooperative threading & I/O loop that's happening) functions that do things like, for example, sleep. So, you might have a my_green_library.sleep and a calls_the_os.sleep; the latter of which will yield the hardware thread directly to the OS, and block that thread completely until its done. Whereas the former will perform a sort of userland context switch, and note something to the I/O loop, and then sleep until the next event. Worse, this problem makes composition hard: you need to know the entire implementation of any function you call, in order to be aware of whether or not it will cause the calling thread to block.
- overgard 12y agoI'm mostly thinking Tornado/Python, where the async stuff happened via generators (IE, the "yield" keyword). But that meant there were large chunks of the python standard library that were basically off limits because they blocked and couldn't be used with a generator, so if you used those functions the main event loop would be stuck waiting. For node, we happen to have a server that calls into a geometric modeler (for collaborative 3d modeling). Since it's doing a lot of math, you could totally conceive that while an expensive modeling operation is running and chewing through CPU cycles, all the other sessions on the system are just waiting. That's kind of a specific use case admittedly, but with threads it wouldn't even be an issue, but with async it's a problem. I get there's ways around it (offload the work to a worker process asynchronously, for instance, which is what we're doing), but it's annoying that it's a thing I have to think about when the functionality is built into the OS.
- rdtsc 12y ago100% agree. Think of your code as an operating system. And heck if you write anything complicated it will start to look like that. As you said, if you write code that needs to co-operatively yield, needs to use defereds, promises, futures, you are back to Windows 3.1 Which is nice, when it came out.[+] The next level in evolution is pre-emptive multi-tasking. A scheduler interrupts your task and make sure to run other task. You don't have to worry about fairness (or another task being unfair). This is your classic threads running in the same shared memory -- C++,Java,C#. In OS equivalent this is Windows 95. It was a glorious OS for its time. It really was, I am not kidding. Next level in evolution is pre-emptive multi-tasking with isolated memory. Aha, now one broken/evil/greedy task can't easily foul up other tasks. This was huge. Think jumping from Window 95 to NT or to Unix/Linux. Awesome things like multi-user system, reliable systems with multiple processes starting/stopping that could run for more than 3 days without rebooting. This is what makes the world run today in general computing. In programming languages paradigm there is ... (sorry, if this is dissapointing) only Erlang. You can add its friends (Elixir, LFE and few others running on same VM). Yap, the only one I know that has built-in heap isolation and light tasks (a couple of K's of memory). The non-programming language specific paradigm is of course OS processes, with some messaging. Now you can call them micro-services living in containers to be cool and that's fine. But is really nice. You've caught up with OS technology finally. [+] : One kind of in-between is semi-pre-emptive multi-tasking and that is when pre-emption happens implicitly during certain system calls (usually IO calls like socket.recv() or say disk.write() or time.sleep(). I think Go works like this and Python's eventlet and gevent work that ways.