Oh what a load of biased crap in that article.

> then you would be infinitely wiser to use an event loop or coroutines.

Coroutines... if you're lucky that your PL of choice supports them in a non-hacky way. C# kind of does with async/await (it's customizable how they behave). Event-loop replaces one evil with another; rewriting otherwise sequential code as a series of callbacks, or (better, somewhat more maintainable) a state machine.

In both cases, one problem is replaced with two others: The first is you have to be careful to not block for a long time in event handlers (or even accidentally invoke blocking I/O that may be hidden in some library you happen to be using). The second is debuggability if your language and tooling doesn't have native support for coroutines; look up "stack ripping".

And then, to top it all, he forgot to tell you that most event loop frameworks allow you to run an event loop on each core, i.e., multithreaded, so you have both events and threads.

> A thread has access to the same address space as other threads in your process, and can do whatever they want with it.

The same holds with any event handler invoked by the event loop. An event handler can just as easily corrupt data belonging to a "wrong" session and the problem will manifest itself 10 minutes later.

> You have to take extreme pains to avoid accidentally doing the wrong thing. You'll probably mess it up, and the symptoms will show up in an unrelated part of the program 10 minutes later.

These sentences assume that whoever is reading this is an ignorant fool. There are tons of books and papers, which have also trickled into mainstream PLs, that teach about writing well-structured and safe threaded code, i.e., there exist patterns for many typical problems.

Actually, I'd argue that with Java and C#, and to a lesser degree modern C++, it's actually _easy_ to write multithreaded code if you think about structure/design first.

You're entering "experts only" area when you start thinking that "mutexes are expensive" and to replace them with interlocked operations.

> But, perhaps your problem is CPU bound. In that case — you still shouldn't use threads! Consider using multiple processes instead.

Oh yes, "great" advice. So you have to decide how these separate processes should communicate.

If you're going for shared memory, you're back to square one, except that sharing is cumbersome because data is mapped to different addresses in different processes, so pointer-based structures can't be shared... At least C++ has a Boost library that mimics the usual containers (vector, map, set) by using special offset-based "pointer type" so they can be safely shared. And you _still_ must synchronize access to shared data.

If you go for the no-shared approach, well, you have to send data through socket, pipe, or a message queue. So you have to design some marshalling and communication protocol (or use a library), but the major drawback here will be performance: each send/receive requires a user-kernel transition, AND, copying data between user- and kernel-space. Twice (once from sender -> kernel, the other time kernel -> receiver).

If the MQ implementation is clever (like on Solaris), the communication buffer will get mapped into both processes so you can get away with copying the data only once, provided the MQ allows you to "peek" and consume data without copying it out from the shared buffer first.

> This is not only much more robust, but it scales better, since you could distribute these programs across multiple computers later on.

This is presented as if distributing across machines is simple matter. What is not mentioned is that communication between processes on the _same_ machine will NEVER fail. As soon as you have network, random latency increases or network outages will kill performance or expose a whole new class of bugs.

> Second, your language designers are lying to you. On a long enough time-scale, all programs will be proven incorrect. All code has bugs, including your compiler and language runtime.

Sure. So go and write in assembly.