Executing Code Concurrently Through Fibers

Although they’re hard to demonstrate usefully in a tiny example, you should know about Crystal’s fibers before going further. They may change the way you structure your programs. In this age of multicore CPUs and distributed computing, developers need their programming language(s) to provide excellent support for concurrency and parallel processing. Unfortunately, this is something Ruby has never been very good at.

In Ruby, you can create multiple threads of execution with Thread.new, but they work on the operating system level. You can have a few hundred of them at most. Moreover, in its C implementation, Ruby is limited by the GIL (the Global interpreter lock), which means that only one Ruby thread can work at a time, so only one core is used. In contrast, Crystal is designed from the ground up to support concurrent and parallel computing (though at the time of this writing, parallel support is still in active development).

Crystal’s concurrency model is based on two concepts:

1) Fibers, which are lightweight threads created by the spawn method and controlled by Crystal itself

2) Channels, through which fibers communicate with each other

The main program thread is a fiber, but other fibers it spawns will work in the background without blocking main. Channels need to know what kind of data goes through them, so they have to be typed. While versions of Ruby since 1.9 support fibers, the scheduling of Crystal fibers is done by Crystal itself instead of by the programmer.

Here’s a simple example that creates a Channel for transporting strings. In a loop that repeats 10,000 times, we spawn a fiber and tell it to send the string "fiber #{i}: I like crystals!" over the channel, so we know which fiber sent it. Then the main fiber receives that string and writes its message to standard output:

 chan = Channel(String).​new
 num = 10000
 num.​times​ ​do​ |i|
  spawn ​do
  chan.​send​ ​"fiber ​​#{​i​}​​: I like crystals!"
 end
  puts chan.​receive
 end
 
 # =>
 # fiber 0: I like crystals!
 # fiber 1: I like crystals!
 # fiber 2: I like crystals!
 # ...
 # fiber 9999: I like crystals!

In practice, of course, there is much more to it than that.

Notice an important distinction between “concurrent” and “parallel” in Crystal conversation: concurrent means that a number of fibers are running in one thread, so executing on one core, called coroutines in many other languages. Parallel, on the other hand, means that two or more code paths are executed at the same time, each on a different core or CPU.

Up to the current version, 0.26.1, Crystal code runs in a single thread. That means it’s concurrent, and it does this well and fast, but it doesn’t yet run in parallel on several cores. The Crystal core team is actively working to make the language run parallel in the near future.

We’ll have a lot more to discuss about this topic in Creating Concurrent Code.

Your Turn 7

Using this example, time how long it takes to spawn 500,000 fibers.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.129.42.134