Under the hood, our “go” function actually spawns a new thread to execute our code block (the loop statement) and returns immediately to call receive on the original channel. The channel, in turn, blocks on receive until the producer has generated a value! In other words, we have just written a multi-threaded producer-consumer, except that there is not a single thread or a mutex in sight! Also take a look at a more interesting multi-worker example, sieve of Eratosthenes, and the results of a non-scientific shootout between MRI and JRuby.
Shared memory, threads and locks have their place and purpose. In fact, if you look under the hood of Agent, or within the source of any runtime with an “alternative concurrency model”, you will undoubtedly find them there at work. So, the question is not whether threads need to exist, but rather, whether they actually make for the best high-level interface to write, test, and manage code that requires concurrency, regardless of runtime.
Ilya’s articles are like candy to me. Go read the whole thing if you haven’t yet.
He hits on an important and oft-missed point in this one. Shared-state threads vs (Actors|Coroutines|Etc.) is often posed as a clash of paradigms, but it’s really comparing two different levels of abstraction.
Threads vs. other concurrency models is more like the comparison of C to Java, or Python, or Ruby. If you did deeply into any high-level dynamic language you are likely to find C in the implementation. Likewise if you look deep enough into any “advanced” concurrency model, you’ll eventually find something that looks like threads, simply because threads are a closer abstraction to the actual CPU architecture – especially when multiprocessing is factored in. You can build any concurrency model you want on top of threads, but not vice-versa.
Which is why it’s a bit unfair to C/C++ programmers when people put down their use of a “flawed” concurrency model. In some cases it’s those very C/C++ programmers who laid the foundations for higher -level concurrency frameworks. E.g the Reactor pattern, used in Ruby’s EventMachine and Python’s Twisted – was originally extracted from work on a C++ networking middleware suite. A lot of work on Actors came from that project as well.
With regard to Ruby, when the rest of the language is so much higher-level than its C building blocks, it is a bit jarring to have the concurrency primitives be threads and mutexes. I agree with Matz’ quote in the article above – it would be nice to have higher level concurrency support in Ruby, and I think that would be more consistent with the rest of the language. Hopefully we’ll see more work along those lines in 2.0.