Core

The structure of this tutorial assumes an intermediate level
knowledge of Python but not much else. No knowledge of
concurrency is expected. The goal is to give you
the tools you need to get going with gevent and use it to solve
or speed up your applications today.

The primary pattern provided by gevent is the Greenlet, a
lightweight coroutine provided to Python as a C extension module.
Greenlets all run inside of the OS process for the main
program but are scheduled cooperatively by libev. This differs from
subprocceses
which are new processes are spawned by the OS.

Greenlets

Synchronous & Asynchronous Execution

The core idea of concurrency is that a larger task can be broken
down into a collection of subtasks whose operation does not
depend on the other tasks and thus can be run
asynchronously instead of one at a time
synchronously. A switch between the two
executions is known as a context swtich.

A context switch in gevent done through
yielding. In this case example we have
two contexts which yield to each other through invoking
gevent.sleep(0).

A somewhat synthetic example defines a task function
which is non-deterministic
(i.e. its output is not guaranteed to give the same result for
the same inputs). In this case the side effect of running the
function is that the task pauses its execution for a random
number of seconds.

In the synchronous case all the tasks are run sequentially,
which results in the main programming blocking (
i.e. pausing the execution of the main program )
while each task executes.

The important parts of the program are the
gevent.spawn which wraps up the given function
inside of a Greenlet thread. The list of initialized greenlets
are stored in the array threads which is passed to
the gevent.joinall function which blocks the current
program to run all the given greenlets. The execution will step
forward only when all the greenlets terminate.

The important fact to notice is that the order of execution in
the async case is essentially random and that the total execution
time in the async case is much less than the sync case. In fact
the maximum time for the synchronous case to complete is when
each tasks pauses for 2 seconds resulting in a 20 seconds for the
whole queue. In the async case the maximum runtime is roughly 2
seconds since none of the tasks block the execution of the
others.

A more common use case, fetching data from a server
asynchronously, the runtime of fetch() will differ between
requests given the load on the remote server.

Race Conditions

The perennial problem involved with concurrency is known as a
race condition. Simply put is when two concurrent threads
/ processes depend on some shared resource but also attempt to
modify this value. This results in resources whose values become
time-dependent on the execution order. This is a problem, and in
general one should very much try to avoid race conditions since
they result program behavior which is globally
non-deterministic.*

One approach to avoiding race conditions is to simply not
have any global state shared between threads. To
communicate threads instead pass stateless messages between each
other.

Spawning Threads

gevent provides a few wrappers around Greenlet initialization.
Some of the most common patterns are:

[[[cog
import gevent
from gevent import Greenlet

def foo(message, n):
"""
Each thread will be passed the message, and n arguments
in its initialization.
"""
gevent.sleep(n)
print(message)

Initialize a new Greenlet instance running the named function

foo

thread1 = Greenlet.spawn(foo, "Hello", 1)

Wrapper for creating and runing a new Greenlet from the named

function foo, with the passd arguments

thread2 = gevent.spawn(foo, "I live!", 2)

Lambda expressions

thread3 = gevent.spawn(lambda x: (x+1), 2)

threads = [thread1, thread2, thread3]

Block until all threads complete.

gevent.joinall(threads)
]]]
[[[end]]]

In addition to using the base Greenlet class, you may also subclass
Greenlet class and overload the _run method.

Exceptions raised in the Greenlet, stay inside the Greenlet.

try:
gevent.joinall([winner, loser])
except Exception as e:
print('This will never be reached')

print(winner.value) # 'You win!'
print(loser.value) # None

print(winner.ready()) # True
print(loser.ready()) # True

print(winner.successful()) # True
print(loser.successful()) # False

The exception raised in fail, will not propogate outside the

greenlet. A stack trace will be printed to stdout but it

will not unwind the stack of the parent.

print(loser.exception)

It is possible though to raise the exception again outside

raise loser.exception

or with

loser.get()

]]]
[[[end]]]

Program Shutdown

Greenlets that fail to yield when the main program receives a
SIGQUIT may hold the program's execution longer than expected.
This results in so called "zombie processes" which need to be
killed from outside of the Python interpreter.

A common pattern is to listen SIGQUIT events on the main program
and to invoke gevent.shutdown before exit.

A extension of the Event object is the AsyncResult which
allows you to send a value along with the wakeup call. This is
sometimes called a future or a deferred, since it holds a
reference to a future value that can be set on an arbitrary time
schedule.

import gevent
from gevent.event import AsyncResult
a = AsyncResult()
def setter():
"""
After 3 seconds set the result of a.
"""
gevent.sleep(3)
a.set('Hello!')
def waiter():
"""
After 3 seconds the get call will unblock after the setter
puts a value into the AsyncResult.
"""
print a.get()
gevent.joinall([
gevent.spawn(setter),
gevent.spawn(waiter),
])

Queues

Queues are ordered sets of data that have the usual put / get
operations but are written in a way such that they can be safely
manipulated across Greenlets.

For example if one Greenlet grabs an item off of the queue, the
same item will not grabbed by another Greenlet executing
simultaneously.

Each of the put and get operations has a non-blocking
counterpart, put_nowait and
get_nowait which will not block, but instead raise
either gevent.queue.Empty or
gevent.queue.Full in the operation is not possible.

In this example we have the boss running simultaneously to the
workers and have a restriction on the Queue that it can contain no
more than three elements. This restriction means that the put
operation will block until there is space on the queue.
Conversely the get operation will block if there are
no elements on the queue to fetch, it also takes a timeout
argument to allow for the queue to exit with the exception
gevent.queue.Empty if no work can found within the
time frame of the Timeout.

Groups and Pools

Locks and Semaphores

Actors

The actor model is a higher level concurrency model popularized
by the language Erlang. In short the main idea is that you have a
collection of independent Actors which have an inbox from which
they receive messages from other Actors. The main loop inside the
Actor iterates through its messages and takes action according to
its desired behavior.

Gevent does not have a primitive Actor type, but we can define
one very simply using a Queue inside of a subclassed Greenlet.

Long Polling

Chat Server

The final motivating example, a realtime chat room. This example
requires Flask ( but not neccesarily so, you could use Django,
Pyramid, etc ). The corresponding Javascript and HTML files can
be found here.