diff --git a/base.css b/base.css index 6b5f6e0..8cfbb6d 100644 --- a/base.css +++ b/base.css @@ -36,8 +36,6 @@ display: block; } body { line-height: 1; } - ol, ul { - list-style: none; } blockquote, q { quotes: none; } blockquote:before, blockquote:after, @@ -102,8 +100,7 @@ /* #Lists ================================================== */ - ul, ol { margin-bottom: 20px; } - ul { list-style: none outside; } + ul, ol { margin-bottom: 20px; padding-left: 20px; } ol { list-style: decimal; } ol, ul.square, ul.circle, ul.disc { margin-left: 30px; } ul.square { list-style: square outside; } @@ -116,6 +113,11 @@ li { line-height: 18px; margin-bottom: 12px; } ul.large li { line-height: 21px; } li p { line-height: 21px; } + #sidebar ul { + list-style: none outside none; + padding-left: 0; + } + /* #Images ================================================== */ diff --git a/gevent.css b/gevent.css index 214dc44..c8eae55 100644 --- a/gevent.css +++ b/gevent.css @@ -17,6 +17,7 @@ a { color: inherit; + text-decoration:none; } pre { @@ -32,7 +33,7 @@ pre code { background: none !important; } -p code { +p code, li code { font: 15px/19px Inconsolata,"Lucida Console",Terminal,"Courier New",Courier; } @@ -40,7 +41,7 @@ p code { display: block; margin-bottom: 0px; padding: 0px; - font-family: sans; + font-family: monospace; font-weight: bold; width: 200px; padding: 0.5em; @@ -52,7 +53,7 @@ p code { h1 { text-align: center; - padding-bottom: 5px; + padding-bottom: 6px; margin-top: 50px; margin-bottom: 50px; } diff --git a/index.html b/index.html index 8d34972..ff3c4f7 100644 --- a/index.html +++ b/index.html @@ -90,7 +90,7 @@

Introduction

your existing concurrency problems and start writing asynchronous applications today.

Contributors

-

In chronological order of contribution: +

In chronological order of contribution: Stephen Diehl Jérémy Bethmont sww @@ -101,20 +101,40 @@

Contributors

youngsterxyf Eddie Hebert Alexis Metaireau -Daniel Velkov

+Daniel Velkov +Sean Wang +Inada Naoki +Balthazar Rouberol +Glen Baker +Jan-Philip Gehrcke +Matthijs van der Vleuten +Simon Hayward +Alexander James Phillips +Ramiro Morales +Philip Damra +Francisco José Marques Vieira +David Xia +satoru +James Summerfield +Adam Szkoda +Roy Smith +Jianbin Wei +Anton Larkin +Matias Herranz +Pietro Bertera

Also thanks to Denis Bilenko for writing gevent and guidance in constructing this tutorial.

This is a collaborative document published under MIT license. Have something to add? See a typo? Fork and issue a pull request Github. Any and all contributions are welcome.

-

This page is also available in Japanese.

+

This page is also available in Japanese and Italian.

Core

Greenlets

The primary pattern used in 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.

+program but are scheduled cooperatively.

Only one greenlet is ever running at any given time.

@@ -124,10 +144,10 @@

Greenlets

are truly parallel.

Synchronous & Asynchronous Execution

The core idea of concurrency is that a larger task can be broken down -into a collection of subtasks whose and scheduled to run simultaneously +into a collection of subtasks which are scheduled to run simultaneously or asynchronously, instead of one at a time or synchronously. A switch between the two subtasks is known as a context switch.

-

A context switch in gevent is done through yielding. In this case +

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


@@ -177,18 +197,18 @@ 

Synchronous & Asynchronous Execu def gr1(): # Busy waits for a second, but we don't want to stick around... - print('Started Polling: ', tic()) + print('Started Polling: %s' % tic()) select.select([], [], [], 2) - print('Ended Polling: ', tic()) + print('Ended Polling: %s' % tic()) def gr2(): # Busy waits for a second, but we don't want to stick around... - print('Started Polling: ', tic()) + print('Started Polling: %s' % tic()) select.select([], [], [], 2) - print('Ended Polling: ', tic()) + print('Ended Polling: %s' % tic()) def gr3(): - print("Hey lets do some stuff while the greenlets poll, at", tic()) + print("Hey lets do some stuff while the greenlets poll, %s" % tic()) gevent.sleep(1) gevent.joinall([ @@ -200,11 +220,11 @@

Synchronous & Asynchronous Execu


-Started Polling:  at 0.0 seconds
-Started Polling:  at 0.0 seconds
-Hey lets do some stuff while the greenlets poll, at at 0.0 seconds
-Ended Polling:  at 2.0 seconds
-Ended Polling:  at 2.0 seconds
+Started Polling: at 0.0 seconds
+Started Polling: at 0.0 seconds
+Hey lets do some stuff while the greenlets poll, at 0.0 seconds
+Ended Polling: at 2.0 seconds
+Ended Polling: at 2.0 seconds
 

Another somewhat synthetic example defines a task function which is non-deterministic @@ -221,10 +241,10 @@

Synchronous & Asynchronous Execu Some non-deterministic task """ gevent.sleep(random.randint(0,2)*0.001) - print('Task', pid, 'done') + print('Task %s done' % pid) def synchronous(): - for i in range(1,10): + for i in xrange(10): task(i) def asynchronous(): @@ -241,6 +261,7 @@

Synchronous & Asynchronous Execu


 Synchronous:
+Task 0 done
 Task 1 done
 Task 2 done
 Task 3 done
@@ -251,16 +272,16 @@ 

Synchronous & Asynchronous Execu Task 8 done Task 9 done Asynchronous: -Task 0 done -Task 9 done Task 1 done -Task 4 done Task 6 done -Task 5 done -Task 8 done +Task 0 done Task 3 done -Task 2 done +Task 4 done +Task 5 done Task 7 done +Task 9 done +Task 2 done +Task 8 done

In the synchronous case all the tasks are run sequentially, which results in the main programming blocking ( @@ -268,7 +289,7 @@

Synchronous & Asynchronous Execu 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 +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 @@ -277,13 +298,13 @@

Synchronous & Asynchronous Execu 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 +each tasks pauses for 0.002 seconds resulting in a 0.02 seconds for the +whole queue. In the async case the maximum runtime is roughly 0.002 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.

+

In a more common use case, asynchronously fetching data from a server, +the runtime of fetch() will differ between +requests, depending on the load on the remote server at the time of the request.

import gevent.monkey
 gevent.monkey.patch_socket()
 
@@ -292,12 +313,12 @@ 

Synchronous & Asynchronous Execu import simplejson as json def fetch(pid): - response = urllib2.urlopen('http://json-time.appspot.com/time.json') + response = urllib2.urlopen('http://jsontime.herokuapp.com/') result = response.read() json_result = json.loads(result) datetime = json_result['datetime'] - print 'Process ', pid, datetime + print('Process %s: %s' % (pid, datetime)) return json_result['datetime'] def synchronous(): @@ -310,19 +331,19 @@

Synchronous & Asynchronous Execu threads.append(gevent.spawn(fetch, i)) gevent.joinall(threads) -print 'Synchronous:' +print('Synchronous:') synchronous() -print 'Asynchronous:' +print('Asynchronous:') asynchronous()

Determinism

As mentioned previously, greenlets are deterministic. Given the same -configuration of greenlets and the same set of inputs and they always -produce the same output. For example lets spread a task across a -multiprocessing pool compared to a gevent pool.

+configuration of greenlets and the same set of inputs, they always +produce the same output. For example, let's spread a task across a +multiprocessing pool and compare its results to the one of a gevent pool.

 
 import time
@@ -341,7 +362,7 @@ 

Determinism

run3 = [a for a in p.imap_unordered(echo, xrange(10))] run4 = [a for a in p.imap_unordered(echo, xrange(10))] -print( run1 == run2 == run3 == run4 ) +print(run1 == run2 == run3 == run4) # Deterministic Gevent Pool @@ -353,7 +374,7 @@

Determinism

run3 = [a for a in p.imap_unordered(echo, xrange(10))] run4 = [a for a in p.imap_unordered(echo, xrange(10))] -print( run1 == run2 == run3 == run4 ) +print(run1 == run2 == run3 == run4)
@@ -369,14 +390,13 @@

Determinism

concurrency", they still can experience some of the same problems that POSIX threads and processes experience.

The perennial problem involved with concurrency is known as a -race condition. Simply put is when two concurrent threads +race condition. Simply put, a race condition occurs when two concurrent threads / processes depend on some shared resource but also attempt to -modify this value. This results in resources whose values become +modify this value. This results in resources which 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.

-

The best approach to this is to simply avoid all global state all +they result in a globally non-deterministic program behavior.

+

The best approach to this is to simply avoid all global state at all times. Global state and import-time side effects will always come back to bite you!

Spawning Greenlets

@@ -398,7 +418,7 @@

Spawning Greenlets

# foo thread1 = Greenlet.spawn(foo, "Hello", 1) -# Wrapper for creating and runing a new Greenlet from the named +# Wrapper for creating and running a new Greenlet from the named # function foo, with the passed arguments thread2 = gevent.spawn(foo, "I live!", 2) @@ -445,17 +465,16 @@

Spawning Greenlets

Greenlet State

Like any other segment of code, Greenlets can fail in various ways. A greenlet may fail to throw an exception, fail to halt or -consume too many system resources.

+consume too many system resources.

The internal state of a greenlet is generally a time-dependent parameter. There are a number of flags on greenlets which let -you monitor the state of the thread

- +you monitor the state of the thread:


 import gevent
@@ -487,7 +506,7 @@ 

Greenlet State

print(winner.successful()) # True print(loser.successful()) # False -# The exception raised in fail, will not propogate outside the +# The exception raised in fail, will not propagate outside the # greenlet. A stack trace will be printed to stdout but it # will not unwind the stack of the parent. @@ -517,7 +536,7 @@

Program Shutdown

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.

+and to invoke gevent.kill or gevent.killall before exit.

 import gevent
 import signal
@@ -526,8 +545,8 @@ 

Program Shutdown

gevent.sleep(1000) if __name__ == '__main__': - gevent.signal(signal.SIGQUIT, gevent.shutdown) thread = gevent.spawn(run_forever) + gevent.signal(signal.SIGQUIT, gevent.kill, thread) thread.join()
@@ -551,12 +570,12 @@

Timeouts

try: gevent.spawn(wait).join() except Timeout: - print 'Could not complete' + print('Could not complete')
-

Or with a context manager in a with statement.

+

They can also be used with a context manager, in a with statement.

 import gevent
 from gevent import Timeout
@@ -572,7 +591,7 @@ 

Timeouts

In addition, gevent also provides timeout arguments for a -variety of Greenlet and data stucture related calls. For example:

+variety of Greenlet and data structure related calls. For example:


 import gevent
 from gevent import Timeout
@@ -616,24 +635,24 @@ 

Timeouts

Monkeypatching

Alas we come to dark corners of Gevent. I've avoided mentioning monkey patching up until now to try and motivate the powerful -coroutine patterns but the time has come to discuss the dark arts -of monkey-patching. If you noticed above we invoked the commnad +coroutine patterns, but the time has come to discuss the dark arts +of monkey-patching. If you noticed above we invoked the command monkey.patch_socket(). This is a purely side-effectful command to -modify the standard library's socket library

+modify the standard library's socket library.

 import socket
-print( socket.socket )
+print(socket.socket)
 
-print "After monkey patch"
+print("After monkey patch")
 from gevent import monkey
 monkey.patch_socket()
-print( socket.socket )
+print(socket.socket)
 
 import select
-print select.select
+print(select.select)
 monkey.patch_select()
-print "After monkey patch"
-print( select.select )
+print("After monkey patch")
+print(select.select)
 
 
@@ -642,15 +661,15 @@

Monkeypatching

After monkey patch class 'gevent.socket.socket' -After monkey patch built-in function select +After monkey patch function select at 0x1924de8

Python's runtime allows for most objects to be modified at runtime including modules, classes, and even functions. This is generally an -astoudingly bad idea since it creates an "implicit side-effect" that is +astoundingly bad idea since it creates an "implicit side-effect" that is most often extremely difficult to debug if problems occur, nevertheless in extreme situations where a library needs to alter the fundamental behavior of Python itself monkey patches can be used. In this case gevent @@ -664,43 +683,52 @@

Monkeypatching

of our gevent stack.

This lets us integrate libraries that would not normally work with gevent without ever writing a single line of code. While monkey-patching -is still evil, in this case it is a "usefull evil".

+is still evil, in this case it is a "useful evil".

Data Structures

Events

Events are a form of asynchronous communication between Greenlets.

 import gevent
-from gevent.event import AsyncResult
+from gevent.event import Event
 
-a = AsyncResult()
+'''
+Illustrates the use of events
+'''
+
+evt = Event()
 
 def setter():
-    """
-    After 3 seconds set wake all threads waiting on the value of
-    a.
-    """
+    '''After 3 seconds, wake all threads waiting on the value of evt'''
+    print('A: Hey wait for me, I have to do something')
     gevent.sleep(3)
-    a.set()
+    print("Ok, I'm done")
+    evt.set()
 
 def waiter():
-    """
-    After 3 seconds the get call will unblock.
-    """
-    a.get() # blocking
-    print 'I live!'
-
-gevent.joinall([
-    gevent.spawn(setter),
-    gevent.spawn(waiter),
-])
+    '''After 3 seconds the get call will unblock'''
+    print("I'll wait for you")
+    evt.wait()  # blocking
+    print("It's about time")
+
+def main():
+    gevent.joinall([
+        gevent.spawn(setter),
+        gevent.spawn(waiter),
+        gevent.spawn(waiter),
+        gevent.spawn(waiter),
+        gevent.spawn(waiter),
+        gevent.spawn(waiter)
+    ])
+
+if __name__ == '__main__': main()
 
 
 
-

A extension of the Event object is the AsyncResult which +

An 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 +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.

@@ -720,7 +748,7 @@ 

Events

After 3 seconds the get call will unblock after the setter puts a value into the AsyncResult. """ - print a.get() + print(a.get()) gevent.joinall([ gevent.spawn(setter), @@ -735,7 +763,7 @@

Queues

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 +same item will not be grabbed by another Greenlet executing simultaneously.


 import gevent
@@ -770,44 +798,44 @@ 

Queues

Worker john got task 2 Worker nancy got task 3 Worker steve got task 4 -Worker nancy got task 5 -Worker john got task 6 +Worker john got task 5 +Worker nancy got task 6 Worker steve got task 7 Worker john got task 8 Worker nancy got task 9 Worker steve got task 10 -Worker nancy got task 11 -Worker john got task 12 +Worker john got task 11 +Worker nancy got task 12 Worker steve got task 13 Worker john got task 14 Worker nancy got task 15 Worker steve got task 16 -Worker nancy got task 17 -Worker john got task 18 +Worker john got task 17 +Worker nancy got task 18 Worker steve got task 19 Worker john got task 20 Worker nancy got task 21 Worker steve got task 22 -Worker nancy got task 23 -Worker john got task 24 +Worker john got task 23 +Worker nancy got task 24 Quitting time! Quitting time! Quitting time!

-

Queues can also block on either put or get as the need arises.

+

Queues can also block on either put or get as the need arises.

Each of the put and get operations has a non-blocking -counterpart, put_nowait and +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.

+gevent.queue.Full if 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 +workers and have a restriction on the Queue preventing it from containing 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 +gevent.queue.Empty if no work can be found within the time frame of the Timeout.


 import gevent
@@ -815,26 +843,28 @@ 

Queues

tasks = Queue(maxsize=3) -def worker(n): +def worker(name): try: while True: task = tasks.get(timeout=1) # decrements queue size by 1 - print('Worker %s got task %s' % (n, task)) + print('Worker %s got task %s' % (name, task)) gevent.sleep(0) except Empty: print('Quitting time!') def boss(): """ - Boss will wait to hand out work until a individual worker is + Boss will wait to hand out work until an individual worker is free since the maxsize of the task queue is 3. """ for i in xrange(1,10): + print('Assigned work %s in iteration 1' % (i)) tasks.put(i) print('Assigned all work in iteration 1') for i in xrange(10,20): + print('Assigned work %s in iteration 2' % (i)) tasks.put(i) print('Assigned all work in iteration 2') @@ -848,25 +878,44 @@

Queues


+Assigned work 1 in iteration 1
+Assigned work 2 in iteration 1
+Assigned work 3 in iteration 1
+Assigned work 4 in iteration 1
 Worker steve got task 1
 Worker john got task 2
 Worker bob got task 3
+Assigned work 5 in iteration 1
+Assigned work 6 in iteration 1
+Assigned work 7 in iteration 1
 Worker steve got task 4
-Worker bob got task 5
-Worker john got task 6
+Worker john got task 5
+Worker bob got task 6
+Assigned work 8 in iteration 1
+Assigned work 9 in iteration 1
 Assigned all work in iteration 1
+Assigned work 10 in iteration 2
 Worker steve got task 7
 Worker john got task 8
 Worker bob got task 9
+Assigned work 11 in iteration 2
+Assigned work 12 in iteration 2
+Assigned work 13 in iteration 2
 Worker steve got task 10
-Worker bob got task 11
-Worker john got task 12
+Worker john got task 11
+Worker bob got task 12
+Assigned work 14 in iteration 2
+Assigned work 15 in iteration 2
+Assigned work 16 in iteration 2
 Worker steve got task 13
 Worker john got task 14
 Worker bob got task 15
+Assigned work 17 in iteration 2
+Assigned work 18 in iteration 2
+Assigned work 19 in iteration 2
 Worker steve got task 16
-Worker bob got task 17
-Worker john got task 18
+Worker john got task 17
+Worker bob got task 18
 Assigned all work in iteration 2
 Worker steve got task 19
 Quitting time!
@@ -910,9 +959,8 @@ 

Groups and Pools

fizz fizz

-

This is very usefull for managing groups of asynchronous tasks -that.

-

As mentioned above Group also provides an API for dispatching +

This is very useful for managing groups of asynchronous tasks.

+

As mentioned above, Group also provides an API for dispatching jobs to grouped greenlets and collecting their results in various ways.


@@ -923,7 +971,7 @@ 

Groups and Pools

group = Group() def hello_from(n): - print('Size of group', len(group)) + print('Size of group %s' % len(group)) print('Hello from Greenlet %s' % id(getcurrent())) group.map(hello_from, xrange(3)) @@ -949,11 +997,11 @@

Groups and Pools


 Size of group 3
-Hello from Greenlet 42121488
+Hello from Greenlet 4405439216
 Size of group 3
-Hello from Greenlet 42119248
+Hello from Greenlet 4405439056
 Size of group 3
-Hello from Greenlet 42120208
+Hello from Greenlet 4405440336
 Ordered
 ('task', 0)
 ('task', 1)
@@ -969,13 +1017,12 @@ 

Groups and Pools

tasks in parallel.


 import gevent
-from gevent import getcurrent
 from gevent.pool import Pool
 
 pool = Pool(2)
 
 def hello_from(n):
-    print('Size of pool', len(pool))
+    print('Size of pool %s' % len(pool))
 
 pool.map(hello_from, xrange(3))
 
@@ -1018,14 +1065,14 @@

Locks and Semaphores

A semaphore is a low level synchronization primitive that allows greenlets to coordinate and limit concurrent access or execution. A semaphore exposes two methods, acquire and release The -difference between the number of times and a semaphore has been +difference between the number of times a semaphore has been acquired and released is called the bound of the semaphore. If a semaphore bound reaches 0 it will block until another greenlet releases its acquisition.


 from gevent import sleep
 from gevent.pool import Pool
-from gevent.coros import BoundedSemaphore
+from gevent.lock import BoundedSemaphore
 
 sem = BoundedSemaphore(2)
 
@@ -1065,8 +1112,8 @@ 

Locks and Semaphores

ensure that resources are only in use at one time in the context of a program.

Thread Locals

-

Gevnet also allows you to specify data which is local the -greenlet context. Internally this is implemented as a global +

Gevent also allows you to specify data which is local to the +greenlet context. Internally, this is implemented as a global lookup which addresses a private namespace keyed by the greenlet's getcurrent() value.


@@ -1100,10 +1147,10 @@ 

Thread Locals

2 x is not local to f2

-

Many web framework thats integrate with gevent store HTTP session -objects inside of gevent thread locals. For example using the +

Many web frameworks that use gevent store HTTP session +objects inside gevent thread locals. For example, using the Werkzeug utility library and its proxy object we can create -Flask style request objects.

+Flask-style request objects.

 from gevent.local import local
 from werkzeug.local import LocalProxy
@@ -1142,34 +1189,50 @@ 

Thread Locals

-

Flask's system is more a bit sophisticated than this example, but the -idea of using thread locals as local session storage is nontheless the +

Flask's system is a bit more sophisticated than this example, but the +idea of using thread locals as local session storage is nonetheless the same.

Subprocess

-

As of Gevent 1.0, support has been added for cooperative waiting -on subprocess.

+

As of gevent 1.0, gevent.subprocess -- a patched version of Python's +subprocess module -- has been added. It supports cooperative waiting on +subprocesses.

-import gevent
+
+import gevent
 from gevent.subprocess import Popen, PIPE
 
-# Uses a green pipe which is cooperative
-sub = Popen(['uname'], stdout=PIPE)
-read_output = gevent.spawn(sub.stdout.read)
-
-output = read_output.join()
-print(output.value)
-
+def cron():
+    while True:
+        print("cron")
+        gevent.sleep(0.2)
+
+g = gevent.spawn(cron)
+sub = Popen(['sleep 1; uname'], stdout=PIPE, shell=True)
+out, err = sub.communicate()
+g.kill()
+print(out.rstrip())
 
-Linux
+
+cron
+cron
+cron
+cron
+cron
+Linux
 
 
-

Many people also want to use gevent and multiprocessing together. This -can be done as most multiprocessing objects expose the underlying file -descriptors.

-

+

Many people also want to use gevent and multiprocessing together. One of +the most obvious challenges is that inter-process communication provided by +multiprocessing is not cooperative by default. Since +multiprocessing.Connection-based objects (such as Pipe) expose their +underlying file descriptors, gevent.socket.wait_read and wait_write can +be used to cooperatively wait for ready-to-read/ready-to-write events before +actually reading/writing:

+
+
 import gevent
 from multiprocessing import Process, Pipe
 from gevent.socket import wait_read, wait_write
@@ -1202,18 +1265,36 @@ 

Subprocess

g1 = gevent.spawn(get_msg) g2 = gevent.spawn(put_msg) gevent.joinall([g1, g2], timeout=1) +
-

-


-

+

Note, however, that the combination of multiprocessing and gevent brings +along certain OS-dependent pitfalls, among others:

+
    +
  • After forking on POSIX-compliant systems +gevent's state in the child is ill-posed. One side effect is that greenlets +spawned before multiprocessing.Process creation run in both, parent and +child process.
  • +
  • a.send() in put_msg() above might still block the calling thread +non-cooperatively: a ready-to-write event only ensures that one byte can be +written. The underlying buffer might be full before the attempted write is +complete.
  • +
  • The wait_write() / wait_read()-based approach as indicated above does +not work on Windows (IOError: 3 is not a socket (files are not supported)), +because Windows cannot watch pipes for events.
  • +
+

The Python package gipc overcomes these +challenges for you in a largely transparent fashion on both, POSIX-compliant and +Windows systems. It provides gevent-aware multiprocessing.Process-based +child processes and gevent-cooperative inter-process communication based on +pipes.

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.

+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.

@@ -1250,13 +1331,13 @@ 

Actors

class Pinger(Actor): def receive(self, message): - print message + print(message) pong.inbox.put('ping') gevent.sleep(0) class Ponger(Actor): def receive(self, message): - print message + print(message) ping.inbox.put('pong') gevent.sleep(0) @@ -1276,19 +1357,18 @@

Gevent ZeroMQ

ZeroMQ is described by its authors as "a socket library that acts as a concurrency framework". It is a very powerful messaging layer for building concurrent and -distributed applications.

+distributed applications.

ZeroMQ provides a variety of socket primitives, the simplest of which being a Request-Response socket pair. A socket has two methods of interest send and recv, both of which are -normally blocking operations. But this is remedied by a briliant -library by Travis Cline which -uses gevent.socket to poll ZeroMQ sockets in a non-blocking -manner. You can install gevent-zeromq from PyPi via: pip install -gevent-zeromq

+normally blocking operations. But this is remedied by a brilliant +library (is now part of PyZMQ) +by Travis Cline which uses gevent.socket +to poll ZeroMQ sockets in a non-blocking manner.


-# Note: Remember to ``pip install pyzmq gevent_zeromq``
+# Note: Remember to ``pip install pyzmq``
 import gevent
-from gevent_zeromq import zmq
+import zmq.green as zmq
 
 # Global Context
 context = zmq.Context()
@@ -1299,7 +1379,7 @@ 

Gevent ZeroMQ

for request in range(1,10): server_socket.send("Hello") - print('Switched to Server for ', request) + print('Switched to Server for %s' % request) # Implicit context switch occurs here server_socket.recv() @@ -1310,7 +1390,7 @@

Gevent ZeroMQ

for request in range(1,10): client_socket.recv() - print('Switched to Client for ', request) + print('Switched to Client for %s' % request) # Implicit context switch occurs here client_socket.send("World") @@ -1323,29 +1403,29 @@

Gevent ZeroMQ


-Switched to Server for  1
-Switched to Client for  1
-Switched to Server for  2
-Switched to Client for  2
-Switched to Server for  3
-Switched to Client for  3
-Switched to Server for  4
-Switched to Client for  4
-Switched to Server for  5
-Switched to Client for  5
-Switched to Server for  6
-Switched to Client for  6
-Switched to Server for  7
-Switched to Client for  7
-Switched to Server for  8
-Switched to Client for  8
-Switched to Server for  9
-Switched to Client for  9
+Switched to Server for 1
+Switched to Client for 1
+Switched to Server for 2
+Switched to Client for 2
+Switched to Server for 3
+Switched to Client for 3
+Switched to Server for 4
+Switched to Client for 4
+Switched to Server for 5
+Switched to Client for 5
+Switched to Server for 6
+Switched to Client for 6
+Switched to Server for 7
+Switched to Client for 7
+Switched to Server for 8
+Switched to Client for 8
+Switched to Server for 9
+Switched to Client for 9
 

Simple Servers

 
-# On Unix: Access with ``$ nc 127.0.0.1 5000`` 
+# On Unix: Access with ``$ nc 127.0.0.1 5000``
 # On Window: Access with ``$ telnet 127.0.0.1 5000``
 
 from gevent.server import StreamServer
@@ -1370,7 +1450,7 @@ 

WSGI Servers

In earlier versions of gevent before 1.0.x, gevent used libevent instead of libev. Libevent included a fast HTTP server which was -used by gevent's wsgi server.

+used by gevent's wsgi server.

In gevent 1.0.x there is no http server included. Instead gevent.wsgi is now an alias for the pure Python server in gevent.pywsgi.

@@ -1439,8 +1519,8 @@

Streaming Servers

But regardless, performance on Gevent servers is phenomenal compared to other Python servers. libev is a very vetted technology and its derivative servers are known to perform well at scale.

-

To benchmark, try Apache Benchmark ab or see this -Benchmark of Python WSGI Servers +

To benchmark, try Apache Benchmark ab or see this +Benchmark of Python WSGI Servers for comparison with other servers.

 $ ab -n 10000 -c 100 http://127.0.0.1:8000/
@@ -1484,7 +1564,7 @@ 

Long Polling

Websockets

-

Websocket example which requires gevent-websocket.

+

Websocket example which requires gevent-websocket.

 # Simple gevent-websocket server
 import json
@@ -1547,7 +1627,7 @@ 

Websockets

Chat Server

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

@@ -1585,7 +1665,7 @@ 

Chat Server

def add(self, message): for user in self.users: - print user + print(user) user.queue.put_nowait(message) self.messages.append(message) @@ -1614,7 +1694,7 @@

Chat Server

active_room = rooms[room] active_room.subscribe(user) - print 'subscribe', active_room, user + print('subscribe %s %s' % (active_room, user)) messages = active_room.backlog() diff --git a/requirements.txt b/requirements.txt index 9a10e26..b17eeac 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,4 @@ Markdown==2.1.1 cogapp==2.3 Jinja2==2.6 gevent -gevent-zeromq +pyzmq diff --git a/tutorial.md b/tutorial.md index 0b66a35..bcf78f9 100644 --- a/tutorial.md +++ b/tutorial.md @@ -11,7 +11,7 @@ applications today. ### Contributors -In chronological order of contribution: +In chronological order of contribution: [Stephen Diehl](http://www.stephendiehl.com) [Jérémy Bethmont](https://github.com/jerem) [sww](https://github.com/sww) @@ -23,6 +23,26 @@ In chronological order of contribution: [Eddie Hebert](https://github.com/ehebert) [Alexis Metaireau](http://notmyidea.org) [Daniel Velkov](https://github.com/djv) +[Sean Wang](https://github.com/sww) +[Inada Naoki](https://github.com/methane) +[Balthazar Rouberol](https://github.com/brouberol) +[Glen Baker](https://github.com/iepathos) +[Jan-Philip Gehrcke](https://gehrcke.de) +[Matthijs van der Vleuten](https://github.com/zr40) +[Simon Hayward](http://simonsblog.co.uk) +[Alexander James Phillips](https://github.com/AJamesPhillips) +[Ramiro Morales](https://github.com/ramiro) +[Philip Damra](https://github.com/djheru) +[Francisco José Marques Vieira](https://github.com/fvieira) +[David Xia](https://www.davidxia.com) +[satoru](https://github.com/satoru) +[James Summerfield](https://github.com/jsummerfield) +[Adam Szkoda](https://github.com/adaszko) +[Roy Smith](https://github.com/roysmith) +[Jianbin Wei](https://github.com/jianbin-netskope) +[Anton Larkin](https://github.com/ToxicWar) +[Matias Herranz](https://github.com/matiasherranz-santex) +[Pietro Bertera](http://www.bertera.it) Also thanks to Denis Bilenko for writing gevent and guidance in constructing this tutorial. @@ -32,7 +52,7 @@ Have something to add? See a typo? Fork and issue a pull request [Github](https://github.com/sdiehl/gevent-tutorial). Any and all contributions are welcome. -This page is also [available in Japanese](http://methane.github.com/gevent-tutorial-ja). +This page is also [available in Japanese](http://methane.github.com/gevent-tutorial-ja) and [Italian](http://pbertera.github.io/gevent-tutorial-it/). # Core @@ -41,7 +61,7 @@ This page is also [available in Japanese](http://methane.github.com/gevent-tutor The primary pattern used in 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. +program but are scheduled cooperatively. > Only one greenlet is ever running at any given time. @@ -53,11 +73,11 @@ are truly parallel. ## Synchronous & Asynchronous Execution The core idea of concurrency is that a larger task can be broken down -into a collection of subtasks whose and scheduled to run simultaneously +into a collection of subtasks which are scheduled to run simultaneously or *asynchronously*, instead of one at a time or *synchronously*. A switch between the two subtasks is known as a *context switch*. -A context switch in gevent is done through *yielding*. In this case +A context switch in gevent is done through *yielding*. In this example we have two contexts which yield to each other through invoking ``gevent.sleep(0)``. @@ -106,18 +126,18 @@ tic = lambda: 'at %1.1f seconds' % (time.time() - start) def gr1(): # Busy waits for a second, but we don't want to stick around... - print('Started Polling: ', tic()) + print('Started Polling: %s' % tic()) select.select([], [], [], 2) - print('Ended Polling: ', tic()) + print('Ended Polling: %s' % tic()) def gr2(): # Busy waits for a second, but we don't want to stick around... - print('Started Polling: ', tic()) + print('Started Polling: %s' % tic()) select.select([], [], [], 2) - print('Ended Polling: ', tic()) + print('Ended Polling: %s' % tic()) def gr3(): - print("Hey lets do some stuff while the greenlets poll, at", tic()) + print("Hey lets do some stuff while the greenlets poll, %s" % tic()) gevent.sleep(1) gevent.joinall([ @@ -144,10 +164,10 @@ def task(pid): Some non-deterministic task """ gevent.sleep(random.randint(0,2)*0.001) - print('Task', pid, 'done') + print('Task %s done' % pid) def synchronous(): - for i in range(1,10): + for i in xrange(10): task(i) def asynchronous(): @@ -169,7 +189,7 @@ 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 +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 @@ -179,14 +199,14 @@ 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 +each tasks pauses for 0.002 seconds resulting in a 0.02 seconds for the +whole queue. In the async case the maximum runtime is roughly 0.002 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. +In a more common use case, asynchronously fetching data from a server, +the runtime of ``fetch()`` will differ between +requests, depending on the load on the remote server at the time of the request.
import gevent.monkey
 gevent.monkey.patch_socket()
@@ -196,12 +216,12 @@ import urllib2
 import simplejson as json
 
 def fetch(pid):
-    response = urllib2.urlopen('http://json-time.appspot.com/time.json')
+    response = urllib2.urlopen('http://jsontime.herokuapp.com/')
     result = response.read()
     json_result = json.loads(result)
     datetime = json_result['datetime']
 
-    print 'Process ', pid, datetime
+    print('Process %s: %s' % (pid, datetime))
     return json_result['datetime']
 
 def synchronous():
@@ -214,10 +234,10 @@ def asynchronous():
         threads.append(gevent.spawn(fetch, i))
     gevent.joinall(threads)
 
-print 'Synchronous:'
+print('Synchronous:')
 synchronous()
 
-print 'Asynchronous:'
+print('Asynchronous:')
 asynchronous()
 
 
@@ -225,9 +245,9 @@ asynchronous() ## Determinism As mentioned previously, greenlets are deterministic. Given the same -configuration of greenlets and the same set of inputs and they always -produce the same output. For example lets spread a task across a -multiprocessing pool compared to a gevent pool. +configuration of greenlets and the same set of inputs, they always +produce the same output. For example, let's spread a task across a +multiprocessing pool and compare its results to the one of a gevent pool.
 
@@ -247,7 +267,7 @@ run2 = [a for a in p.imap_unordered(echo, xrange(10))]
 run3 = [a for a in p.imap_unordered(echo, xrange(10))]
 run4 = [a for a in p.imap_unordered(echo, xrange(10))]
 
-print( run1 == run2 == run3 == run4 )
+print(run1 == run2 == run3 == run4)
 
 # Deterministic Gevent Pool
 
@@ -259,7 +279,7 @@ run2 = [a for a in p.imap_unordered(echo, xrange(10))]
 run3 = [a for a in p.imap_unordered(echo, xrange(10))]
 run4 = [a for a in p.imap_unordered(echo, xrange(10))]
 
-print( run1 == run2 == run3 == run4 )
+print(run1 == run2 == run3 == run4)
 
 
@@ -276,15 +296,14 @@ concurrency", they still can experience some of the same problems that POSIX threads and processes experience. The perennial problem involved with concurrency is known as a -*race condition*. Simply put is when two concurrent threads +*race condition*. Simply put, a race condition occurs when two concurrent threads / processes depend on some shared resource but also attempt to -modify this value. This results in resources whose values become +modify this value. This results in resources which 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. +they result in a globally non-deterministic program behavior. -The best approach to this is to simply avoid all global state all +The best approach to this is to simply avoid all global state at all times. Global state and import-time side effects will always come back to bite you! @@ -309,7 +328,7 @@ def foo(message, n): # foo thread1 = Greenlet.spawn(foo, "Hello", 1) -# Wrapper for creating and runing a new Greenlet from the named +# Wrapper for creating and running a new Greenlet from the named # function foo, with the passed arguments thread2 = gevent.spawn(foo, "I live!", 2) @@ -352,17 +371,17 @@ g.join() Like any other segment of code, Greenlets can fail in various ways. A greenlet may fail to throw an exception, fail to halt or -consume too many system resources.

+consume too many system resources. -

The internal state of a greenlet is generally a time-dependent +The internal state of a greenlet is generally a time-dependent parameter. There are a number of flags on greenlets which let -you monitor the state of the thread

+you monitor the state of the thread: -- ``started`` -- Boolean, indicates whether the Greenlet has been started. -- ``ready()`` -- Boolean, indicates whether the Greenlet has halted -- ``successful()`` -- Boolean, indicates whether the Greenlet has halted and not thrown an exception -- ``value`` -- arbitrary, the value returned by the Greenlet -- ``exception`` -- exception, uncaught exception instance thrown inside the greenlet +- ``started`` -- Boolean, indicates whether the Greenlet has been started +- ``ready()`` -- Boolean, indicates whether the Greenlet has halted +- ``successful()`` -- Boolean, indicates whether the Greenlet has halted and not thrown an exception +- ``value`` -- arbitrary, the value returned by the Greenlet +- ``exception`` -- exception, uncaught exception instance thrown inside the greenlet [[[cog import gevent @@ -394,7 +413,7 @@ print(loser.ready()) # True print(winner.successful()) # True print(loser.successful()) # False -# The exception raised in fail, will not propogate outside the +# The exception raised in fail, will not propagate outside the # greenlet. A stack trace will be printed to stdout but it # will not unwind the stack of the parent. @@ -415,7 +434,7 @@ 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. +and to invoke ``gevent.kill`` or ``gevent.killall`` before exit.
 import gevent
@@ -425,8 +444,8 @@ def run_forever():
     gevent.sleep(1000)
 
 if __name__ == '__main__':
-    gevent.signal(signal.SIGQUIT, gevent.shutdown)
     thread = gevent.spawn(run_forever)
+    gevent.signal(signal.SIGQUIT, gevent.kill, thread)
     thread.join()
 
 
@@ -452,12 +471,12 @@ def wait(): try: gevent.spawn(wait).join() except Timeout: - print 'Could not complete' + print('Could not complete')
-Or with a context manager in a ``with`` statement. +They can also be used with a context manager, in a ``with`` statement.
 import gevent
@@ -474,7 +493,7 @@ with Timeout(time_to_wait, TooLong):
 
In addition, gevent also provides timeout arguments for a -variety of Greenlet and data stucture related calls. For example: +variety of Greenlet and data structure related calls. For example: [[[cog import gevent @@ -515,25 +534,25 @@ except Timeout: Alas we come to dark corners of Gevent. I've avoided mentioning monkey patching up until now to try and motivate the powerful -coroutine patterns but the time has come to discuss the dark arts -of monkey-patching. If you noticed above we invoked the commnad +coroutine patterns, but the time has come to discuss the dark arts +of monkey-patching. If you noticed above we invoked the command ``monkey.patch_socket()``. This is a purely side-effectful command to -modify the standard library's socket library +modify the standard library's socket library.
 import socket
-print( socket.socket )
+print(socket.socket)
 
-print "After monkey patch"
+print("After monkey patch")
 from gevent import monkey
 monkey.patch_socket()
-print( socket.socket )
+print(socket.socket)
 
 import select
-print select.select
+print(select.select)
 monkey.patch_select()
-print "After monkey patch"
-print( select.select )
+print("After monkey patch")
+print(select.select)
 
 
@@ -550,7 +569,7 @@ function select at 0x1924de8 Python's runtime allows for most objects to be modified at runtime including modules, classes, and even functions. This is generally an -astoudingly bad idea since it creates an "implicit side-effect" that is +astoundingly bad idea since it creates an "implicit side-effect" that is most often extremely difficult to debug if problems occur, nevertheless in extreme situations where a library needs to alter the fundamental behavior of Python itself monkey patches can be used. In this case gevent @@ -566,7 +585,7 @@ of our gevent stack. This lets us integrate libraries that would not normally work with gevent without ever writing a single line of code. While monkey-patching -is still evil, in this case it is a "usefull evil". +is still evil, in this case it is a "useful evil". # Data Structures @@ -577,36 +596,47 @@ Greenlets.
 import gevent
-from gevent.event import AsyncResult
+from gevent.event import Event
 
-a = AsyncResult()
+'''
+Illustrates the use of events
+'''
+
+
+evt = Event()
 
 def setter():
-    """
-    After 3 seconds set wake all threads waiting on the value of
-    a.
-    """
-    gevent.sleep(3)
-    a.set()
+    '''After 3 seconds, wake all threads waiting on the value of evt'''
+	print('A: Hey wait for me, I have to do something')
+	gevent.sleep(3)
+	print("Ok, I'm done")
+	evt.set()
 
-def waiter():
-    """
-    After 3 seconds the get call will unblock.
-    """
-    a.get() # blocking
-    print 'I live!'
 
-gevent.joinall([
-    gevent.spawn(setter),
-    gevent.spawn(waiter),
-])
+def waiter():
+	'''After 3 seconds the get call will unblock'''
+	print("I'll wait for you")
+	evt.wait()  # blocking
+	print("It's about time")
+
+def main():
+	gevent.joinall([
+		gevent.spawn(setter),
+		gevent.spawn(waiter),
+		gevent.spawn(waiter),
+		gevent.spawn(waiter),
+		gevent.spawn(waiter),
+		gevent.spawn(waiter)
+	])
+
+if __name__ == '__main__': main()
 
 
 
-A extension of the Event object is the AsyncResult which +An 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 +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. @@ -627,7 +657,7 @@ def waiter(): After 3 seconds the get call will unblock after the setter puts a value into the AsyncResult. """ - print a.get() + print(a.get()) gevent.joinall([ gevent.spawn(setter), @@ -644,7 +674,7 @@ 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 +same item will not be grabbed by another Greenlet executing simultaneously. [[[cog @@ -675,22 +705,22 @@ gevent.joinall([ ]]] [[[end]]] -Queues can also block on either ``put`` or ``get`` as the need arises. +Queues can also block on either ``put`` or ``get`` as the need arises. Each of the ``put`` and ``get`` operations has a non-blocking -counterpart, ``put_nowait`` and +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. +``gevent.queue.Full`` if 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 +workers and have a restriction on the Queue preventing it from containing 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 +``gevent.queue.Empty`` if no work can be found within the time frame of the Timeout. [[[cog @@ -699,26 +729,28 @@ from gevent.queue import Queue, Empty tasks = Queue(maxsize=3) -def worker(n): +def worker(name): try: while True: task = tasks.get(timeout=1) # decrements queue size by 1 - print('Worker %s got task %s' % (n, task)) + print('Worker %s got task %s' % (name, task)) gevent.sleep(0) except Empty: print('Quitting time!') def boss(): """ - Boss will wait to hand out work until a individual worker is + Boss will wait to hand out work until an individual worker is free since the maxsize of the task queue is 3. """ for i in xrange(1,10): + print('Assigned work %s in iteration 1' % (i)) tasks.put(i) print('Assigned all work in iteration 1') for i in xrange(10,20): + print('Assigned work %s in iteration 2' % (i)) tasks.put(i) print('Assigned all work in iteration 2') @@ -759,10 +791,9 @@ group.join() ]]] [[[end]]] -This is very usefull for managing groups of asynchronous tasks -that. +This is very useful for managing groups of asynchronous tasks. -As mentioned above Group also provides an API for dispatching +As mentioned above, ``Group`` also provides an API for dispatching jobs to grouped greenlets and collecting their results in various ways. @@ -774,7 +805,7 @@ from gevent.pool import Group group = Group() def hello_from(n): - print('Size of group', len(group)) + print('Size of group %s' % len(group)) print('Hello from Greenlet %s' % id(getcurrent())) group.map(hello_from, xrange(3)) @@ -806,13 +837,12 @@ tasks in parallel. [[[cog import gevent -from gevent import getcurrent from gevent.pool import Pool pool = Pool(2) def hello_from(n): - print('Size of pool', len(pool)) + print('Size of pool %s' % len(pool)) pool.map(hello_from, xrange(3)) ]]] @@ -852,7 +882,7 @@ class SocketPool(object): A semaphore is a low level synchronization primitive that allows greenlets to coordinate and limit concurrent access or execution. A semaphore exposes two methods, ``acquire`` and ``release`` The -difference between the number of times and a semaphore has been +difference between the number of times a semaphore has been acquired and released is called the bound of the semaphore. If a semaphore bound reaches 0 it will block until another greenlet releases its acquisition. @@ -860,7 +890,7 @@ releases its acquisition. [[[cog from gevent import sleep from gevent.pool import Pool -from gevent.coros import BoundedSemaphore +from gevent.lock import BoundedSemaphore sem = BoundedSemaphore(2) @@ -890,8 +920,8 @@ of a program. ## Thread Locals -Gevnet also allows you to specify data which is local the -greenlet context. Internally this is implemented as a global +Gevent also allows you to specify data which is local to the +greenlet context. Internally, this is implemented as a global lookup which addresses a private namespace keyed by the greenlet's ``getcurrent()`` value. @@ -921,10 +951,10 @@ gevent.joinall([g1, g2]) ]]] [[[end]]] -Many web framework thats integrate with gevent store HTTP session -objects inside of gevent thread locals. For example using the +Many web frameworks that use gevent store HTTP session +objects inside gevent thread locals. For example, using the Werkzeug utility library and its proxy object we can create -Flask style request objects. +Flask-style request objects.
 from gevent.local import local
@@ -965,38 +995,54 @@ WSGIServer(('', 8000), application).serve_forever()
 
 
-Flask's system is more a bit sophisticated than this example, but the -idea of using thread locals as local session storage is nontheless the +Flask's system is a bit more sophisticated than this example, but the +idea of using thread locals as local session storage is nonetheless the same. ## Subprocess -As of Gevent 1.0, support has been added for cooperative waiting -on subprocess. +As of gevent 1.0, ``gevent.subprocess`` -- a patched version of Python's +``subprocess`` module -- has been added. It supports cooperative waiting on +subprocesses.
-import gevent
+
+import gevent
 from gevent.subprocess import Popen, PIPE
 
-# Uses a green pipe which is cooperative
-sub = Popen(['uname'], stdout=PIPE)
-read_output = gevent.spawn(sub.stdout.read)
-
-output = read_output.join()
-print(output.value)
-
+def cron():
+    while True:
+        print("cron")
+        gevent.sleep(0.2)
+
+g = gevent.spawn(cron)
+sub = Popen(['sleep 1; uname'], stdout=PIPE, shell=True)
+out, err = sub.communicate()
+g.kill()
+print(out.rstrip())
 
-Linux
+
+cron
+cron
+cron
+cron
+cron
+Linux
 
 
-Many people also want to use gevent and multiprocessing together. This -can be done as most multiprocessing objects expose the underlying file -descriptors. +Many people also want to use ``gevent`` and ``multiprocessing`` together. One of +the most obvious challenges is that inter-process communication provided by +``multiprocessing`` is not cooperative by default. Since +``multiprocessing.Connection``-based objects (such as ``Pipe``) expose their +underlying file descriptors, ``gevent.socket.wait_read`` and ``wait_write`` can +be used to cooperatively wait for ready-to-read/ready-to-write events before +actually reading/writing: -[[[cog +
+
 import gevent
 from multiprocessing import Process, Pipe
 from gevent.socket import wait_read, wait_write
@@ -1029,8 +1075,29 @@ if __name__ == '__main__':
     g1 = gevent.spawn(get_msg)
     g2 = gevent.spawn(put_msg)
     gevent.joinall([g1, g2], timeout=1)
-]]]
-[[[end]]]
+
+
+ +Note, however, that the combination of ``multiprocessing`` and gevent brings +along certain OS-dependent pitfalls, among others: + +* After [forking](http://linux.die.net/man/2/fork) on POSIX-compliant systems +gevent's state in the child is ill-posed. One side effect is that greenlets +spawned before ``multiprocessing.Process`` creation run in both, parent and +child process. +* ``a.send()`` in ``put_msg()`` above might still block the calling thread +non-cooperatively: a ready-to-write event only ensures that one byte can be +written. The underlying buffer might be full before the attempted write is +complete. +* The ``wait_write()`` / ``wait_read()``-based approach as indicated above does +not work on Windows (``IOError: 3 is not a socket (files are not supported)``), +because Windows cannot watch pipes for events. + +The Python package [gipc](http://pypi.python.org/pypi/gipc) overcomes these +challenges for you in a largely transparent fashion on both, POSIX-compliant and +Windows systems. It provides gevent-aware ``multiprocessing.Process``-based +child processes and gevent-cooperative inter-process communication based on +pipes. ## Actors @@ -1039,7 +1106,7 @@ 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. +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. @@ -1080,13 +1147,13 @@ from gevent import Greenlet class Pinger(Actor): def receive(self, message): - print message + print(message) pong.inbox.put('ping') gevent.sleep(0) class Ponger(Actor): def receive(self, message): - print message + print(message) ping.inbox.put('pong') gevent.sleep(0) @@ -1108,21 +1175,20 @@ gevent.joinall([ping, pong]) [ZeroMQ](http://www.zeromq.org/) is described by its authors as "a socket library that acts as a concurrency framework". It is a very powerful messaging layer for building concurrent and -distributed applications. +distributed applications. ZeroMQ provides a variety of socket primitives, the simplest of which being a Request-Response socket pair. A socket has two methods of interest ``send`` and ``recv``, both of which are -normally blocking operations. But this is remedied by a briliant -library by [Travis Cline](https://github.com/traviscline) which -uses gevent.socket to poll ZeroMQ sockets in a non-blocking -manner. You can install gevent-zeromq from PyPi via: ``pip install -gevent-zeromq`` +normally blocking operations. But this is remedied by a brilliant +[library](https://github.com/tmc/gevent-zeromq) (is now part of PyZMQ) +by [Travis Cline](https://github.com/tmc) which uses gevent.socket +to poll ZeroMQ sockets in a non-blocking manner. [[[cog -# Note: Remember to ``pip install pyzmq gevent_zeromq`` +# Note: Remember to ``pip install pyzmq`` import gevent -from gevent_zeromq import zmq +import zmq.green as zmq # Global Context context = zmq.Context() @@ -1133,7 +1199,7 @@ def server(): for request in range(1,10): server_socket.send("Hello") - print('Switched to Server for ', request) + print('Switched to Server for %s' % request) # Implicit context switch occurs here server_socket.recv() @@ -1144,7 +1210,7 @@ def client(): for request in range(1,10): client_socket.recv() - print('Switched to Client for ', request) + print('Switched to Client for %s' % request) # Implicit context switch occurs here client_socket.send("World") @@ -1160,8 +1226,8 @@ gevent.joinall([publisher, client])
 
-# On Unix: Access with ``$ nc 127.0.0.1 5000`` 
-# On Window: Access with ``$ telnet 127.0.0.1 5000`` 
+# On Unix: Access with ``$ nc 127.0.0.1 5000``
+# On Window: Access with ``$ telnet 127.0.0.1 5000``
 
 from gevent.server import StreamServer
 
@@ -1186,7 +1252,7 @@ Henceforth called ``wsgi`` and ``pywsgi``:
 
 In earlier versions of gevent before 1.0.x, gevent used libevent
 instead of libev. Libevent included a fast HTTP server which was
-used by gevent's ``wsgi`` server. 
+used by gevent's ``wsgi`` server.
 
 In gevent 1.0.x there is no http server included. Instead
 ``gevent.wsgi`` is now an alias for the pure Python server in
@@ -1236,7 +1302,7 @@ def application(environ, start_response):
 WSGIServer(('', 8000), application).serve_forever()
 
 
-
+
Using pywsgi we can however write our handler as a generator and yield the result chunk by chunk. @@ -1258,20 +1324,20 @@ def application(environ, start_response): WSGIServer(('', 8000), application).serve_forever() -
+
But regardless, performance on Gevent servers is phenomenal compared to other Python servers. libev is a very vetted technology and its derivative servers are known to perform well at scale. -To benchmark, try Apache Benchmark ``ab`` or see this -[Benchmark of Python WSGI Servers](http://nichol.as/benchmark-of-python-web-servers) +To benchmark, try Apache Benchmark ``ab`` or see this +[Benchmark of Python WSGI Servers](http://nichol.as/benchmark-of-python-web-servers) for comparison with other servers.
 $ ab -n 10000 -c 100 http://127.0.0.1:8000/
 
-
+
## Long Polling @@ -1313,7 +1379,7 @@ WSGIServer(('', 8000), ajax_endpoint).serve_forever() ## Websockets -Websocket example which requires gevent-websocket. +Websocket example which requires gevent-websocket.
@@ -1381,7 +1447,7 @@ HTML Page:
 ## Chat Server
 
 The final motivating example, a realtime chat room. This example
-requires Flask ( but not neccesarily so, you could use Django,
+requires Flask ( but not necessarily so, you could use Django,
 Pyramid, etc ). The corresponding Javascript and HTML files can
 be found here.
 
@@ -1421,7 +1487,7 @@ class Room(object):
 
     def add(self, message):
         for user in self.users:
-            print user
+            print(user)
             user.queue.put_nowait(message)
         self.messages.append(message)
 
@@ -1450,7 +1516,7 @@ def join(room, uid):
 
     active_room = rooms[room]
     active_room.subscribe(user)
-    print 'subscribe', active_room, user
+    print('subscribe %s %s' % (active_room, user))
 
     messages = active_room.backlog()