Fibers, Threads and Continuations
The difference between them is not always obvious, so let’s have a quick recap.
Threads
Threads are a simple way to have multiple sections of code being ran at the same time. The purist will now complain and tell us about the GIL and green threads, but for the sake of this post let’s keep things simple.
This is the output:
Starting thread 0
Starting thread 1
Starting thread 2
Finishing thread 1
Finishing thread 2
Finishing thread 0
BTW: have you noticed the Thread.new(i) do |x|
? Any arguments passed to the Thread constructor are passed to the block, and we do this to make sure we’re using a local variable (in the thread scope) rather than i
, that belongs to the outer scope and that could change without us noticing!
BTW#2: what’s the join
for? It’s there to make sure the thread is completed before terminating the program, otherwise the threads will be killed without waiting for them.
Shared variables
Local variables are local to the thread; if you need a shared variable across threads you can use the current thread object as a hash:
Thread methods
stop
: Stops execution of the current thread, putting it into a “sleep” state, and schedules execution of another thread.run
: Wakes up thr, making it eligible for schedulingjoin
: The calling thread will suspend execution and run thr. Does not return until thr exits or until limit seconds have passed. If the time limit expires, nil will be returned, otherwise thr is returned.pass
: Give the thread scheduler a hint to pass execution to another thread. A running thread may or may not switch, it depends on OS and processor.value
: Waits for thr to complete (via Thread#join) and returns its value.
Fibers
Fibers have been introduced in Ruby 1.9. They can be considered a lightweight thread. The main difference is that with threads the runtime takes care of running them and switching them (although you can control the thread scheduler with stop
/run
etc), while with fibers we have to manually start and stop their execution (you have no preemption). The documentation is very clear:
Fibers are primitives for implementing light weight cooperative concurrency in Ruby. Basically they are a means of creating code blocks that can be paused and resumed, much like threads. The main difference is that they are never preempted and that the scheduling must be done by the programmer and not the VM.
When a fiber is created it will not run automatically. Rather it must be be explicitly asked to run using the Fiber#resume method. The code running inside the fiber can give up control by calling Fiber.yield in which case it yields control back to caller (the caller of the Fiber#resume).
So the two main methods you use on a fiber are resume
and yield
. Here is a simple example:
The output is:
before yield
in between
after yield
So the fiber is first manually started, then it returns the control to the caller, then the caller makes it continue.
Continuations
Continuations are a concept that comes from the functional programming world (but they exist in C, too, with setjmp
and longjmp
). They’re the functional equivalent of the dreaded GOTO statement.
In ruby they are an object that stores a return address and an execution context (you can think to it as a binding). They can be used to save the state of the running program and resume it afterwards.
let’s make a concrete example, since all those I’ve found on the web weren’t clean enough to me:
The callcc
method returns a Continuation object. The code block will be executed right away:
Output:
1
2
3
4
When you call the call
method on the Continuation object, then the code execution will resume at the end of the block! If you make such a call inside the block itself, then the block execution stops, just like it was finished.
Output:
1
2
4
So far this seems still pretty lame. It becomes interesting when we start passing continuation objects around: this allows us to GOTO through different parts of the app:
Do you remember the old BASIC loop?
10 PRINT "hello"
20 GOTO 10
Well here it is in Ruby:
Links
- http://masanjin.net/blog/fibers