eventmachine/eventmachine

View on GitHub
docs/old/LIGHTWEIGHT_CONCURRENCY

Summary

Maintainability
Test Coverage
EventMachine (EM) adds two different formalisms for lightweight concurrency to
the Ruby programmer's toolbox: spawned processes and deferrables. This note
will show you how to use them.


=== What is Lightweight Concurrency?

We use the term "Lightweight Concurrency" (LC) to refer to concurrency
mechanisms that are lighter than Ruby threads. By "lighter," we mean: less
resource-intensive in one or more dimensions, usually including memory and
CPU usage. In general, you turn to LC in the hope of improving the
performance and scalability of your programs.

In addition to the two EventMachine mechanisms we will discuss here, Ruby
has at least one other LC construct: Fibers, which are currently under
development in Ruby 1.9.

The technical feature that makes all of these LC mechanisms different from
standard Ruby threads is that they are not scheduled automatically.

When you create and run Ruby threads, you can assume (within certain
constraints) that your threads will all be scheduled fairly by Ruby's runtime.
Ruby itself is responsible for giving each of your threads its own share of
the total runtime.

But with LC, your program is responsible for causing different execution
paths to run. In effect, your program has to act as a "thread scheduler."
Scheduled entities in LC run to completion and are never preempted. The
runtime system has far less work to do since it has no need to interrupt
threads or to schedule them fairly. This is what makes LC lighter and faster.

You'll learn exactly how LC scheduling works in practice as we work through
specific examples.


=== EventMachine Lightweight Concurrency

Recall that EM provides a reactor loop that must be running in order for
your programs to perform event-driven logic. An EM program typically has a
structure like this:

 require 'eventmachine'

 # your initializations

 EM.run {
     # perform event-driven I/O here, including network clients,
     # servers, timers, and thread-pool operations.
 }

 # your cleanup
 # end of the program


EventMachine#run executes the reactor loop, which causes your code to be
called as events of interest to your program occur. The block you pass to
EventMachine#run is executed right after the reactor loop starts, and is
the right place to start socket acceptors, etc.

Because the reactor loop runs constantly in an EM program (until it is
stopped by a call to EventMachine#stop), it has the ability to schedule
blocks of code for asynchronous execution. Unlike a pre-emptive thread
scheduler, it's NOT able to interrupt code blocks while they execute. But
the scheduling capability it does have is enough to enable lightweight
concurrency.


For information on Spawned Processes, see the separate document
SPAWNED_PROCESSES.

For information on Deferrables, see the separate document DEFERRABLES.


=== [SIDEBAR]: I Heard That EventMachine Doesn't Work With Ruby Threads.

This is incorrect. EM is fully interoperable with all versions of Ruby
threads, and has been since its earliest releases.

It's very true that EM encourages an "evented" (non-threaded) programming
style. The specific benefits of event-driven programming are far better
performance and scalability for well-written programs, and far easier
debugging.

The benefit of using threads for similar applications is a possibly more
intuitive programming model, as well as the fact that threads are already
familiar to most programmers. Also, bugs in threaded programs often fail
to show up until programs go into production. These factors create the
illusion that threaded programs are easier to write.

However, some operations that occur frequently in professional-caliber
applications simply can't be done without threads. (The classic example
is making calls to database client-libraries that block on network I/O
until they complete.)

EventMachine not only allows the use of Ruby threads in these cases, but
it even provides a built-in thread-pool object to make them easier to
work with.

You may have heard a persistent criticism that evented I/O is fundamentally
incompatible with Ruby threads. It is true that some well-publicized attempts
to incorporate event-handling libraries into Ruby were not successful. But
EventMachine was designed from the ground up with Ruby compatibility in mind,
so EM never suffered from the problems that defeated the earlier attempts.


=== [SIDEBAR]: I Heard That EventMachine Doesn't Work Very Well On Windows.

This too is incorrect. EventMachine is an extension written in C++ and Java,
and therefore it requires compilation. Many Windows computers (and some Unix
computers, especially in production environments) don't have a build stack.
Attempting to install EventMachine on a machine without a compiler usually
produces a confusing error.

In addition, Ruby has a much-debated issue with Windows compiler versions.
Ruby on Windows works best with Visual Studio 6, a compiler version that is
long out-of-print, no longer supported by Microsoft, and difficult to obtain.
(This problem is not specific to EventMachine.)

Shortly after EventMachine was first released, the compiler issues led to
criticism that EM was incompatible with Windows. Since that time, every
EventMachine release has been supplied in a precompiled binary form for
Windows users, that does not require you to compile the code yourself. EM
binary Gems for Windows are compiled using Visual Studio 6.

EventMachine does supply some advanced features (such as Linux EPOLL support,
reduced-privilege operation, UNIX-domain sockets, etc.) that have no
meaningful implementation on Windows. Apart from these special cases, all EM
functionality (including lightweight concurrency) works perfectly well on
Windows.