"But there are other forms of concurrency. Processes that communicate using files, pipes, sockets, or even shared memory."<p>I don't see how this solves the problem. It's true that some problems work well as pipelines where each stage does some finite, balanced amount of work before passing the data off to the next stage. But many don't.<p>And I don't follow the files and shared memory argument at all: those are both shared, unsynchronized-in-the-base-API abstractions <i>just</i> <i>like</i> <i>threading</i>. And they're subject to all the same deadlocks and race conditions. The only difference is that you use slightly different synchronization primitives (e.g. fcntl() locks instead of mutex locks).<p>It's correct to point out that traditional threaded code is difficult to get right. But it's naive and unhelpful to point to any kind of "secret sauce" as the solution. The difficulty of concurrency lies in the <i>problem</i> <i>itself</i>, not the API.