Also, most languages don't have a good way of dealing with callbacks. They tend to make the code verbose and difficult to reason about. This is especially true for non-trivial applications, where there are more than two or three callbacks chained together.
However, you can counter-argue that the additional complexity isn't endemic to the programming model, only certain languages. Also, in languages which support multiple threads (read: not Javascript), a hybrid approach that uses events where possible, and threads where necessary, may retain many of the benefits of the event-based approach.
This is one of the main reasons I like node.js, EVERYTHING is evented, if it isn't it's probably a bug, or at least carefully explained why it can't be (and usually there's an async alternative).
Serially:
int handle_connection(int fd) {
...
}
int loop() {
...
while(1) {
fd = accept(listener);
handle_connection(fd);
}
}
Threaded: int handle_connection(int fd) {
...
}
int loop() {
...
while(1) {
fd = accept(listener);
thread_start(handle_connection, fd);
}
}Evented:
int handle_connection(int fd) {
...
}
int doaccept() {
put_on_event_loop(accept(listener));
}Probably because they weren't popular. And they weren't popular because they were hard to write in "classic" languages. I'd never try to write a complex asynchronous server in a language like C, or Java. But then, I'd never try to write a thread-per-connection server in Python. Limiting ourselves to popular languages only: "old" ones didn't provide good support for this kind of thing. In "new" ones evented servers are trivial with closures, simulated continuations, etc. This might seem silly, but look at the slides 36 from "Paul Tyma" mentioned in another comment. "Found that when switching between clients, the code for saving and restoring values/state was difficult" - I'm not even sure what he means by that... why save / restore? That seems like a seriously difficult way to write the code.
Then again there are places where you really shouldn't choose one over another, unless you've got a really good reason. For example in telecommunication when you're dealing with signalling, doing thread per connection is close to insane. That's one of the reasons there's only a handful of people who understand chan_sip in the Asterisk project and why it's full of DEADLOCK_AVOIDANCE macros.
In the case of node.js for example a lot of libraries (mysql for example) have to be wrapped or rewritten since it would block the whole event loop.
(* t (/ 1000 w))
or (perhaps typeset properly) t * 1000 / wMany programmers dislike math notation; they have bad experiences of school, and gloss over anything which reminds them of dull homework and tests. On the other hand, programming notation symbolizes a feeling of freedom.
It helps that I'm more used to Clojure's notation than traditional math notation. Also it's easily executable for me. No need to execute precise arithmetic mentally; the computer can do it.
For the level of analysis in the article, there's really no difference between a 4 core machine and a processor with a 4x clock speed. His point is that the threaded model makes the most sense when each request is CPU intensive, and the event model works best when the work is light but the delays are long. All that would change for a multi-core processor is the definition of when the works starts to be CPU 'intensive'.
Edit: My point is that, since we live in a real tangible world, where CPU power is being expanded by parallelism rather then increasing single pipeline throughput, we have to deal with that reality. To dismiss it to prove your point, seems a tad bit naive.
An evented server (at least, until they get a lot fancier and make you program with locks and such) only gets to use one of the cores, while a threaded server gets all four.
Using a single core just means that the threaded server doesn't get any advantage from being threaded, while the evented server gets everything it can use.
This "model" is worthless for comparison.
I find it unrealistic that threaded vs evented (or blocking vs non-blocking I/O) comparisons always use a slow database server as the prototypical thing to wait for.
I get that this is not the point but to a newcomer it must seem like "oh.. database servers are super slow, I must first and foremost worry about optimizing access to them".
If your queries regularly take more then 10ms to complete, something is wrong with the database (do caching, put database closer to querying server - maybe even on the same machine).