@@ -141,7 +141,7 @@ <h3 class="author">
141141< li > < a href ="#core "> Core</ a > < ul >
142142< li > < a href ="#greenlets "> Greenlets</ a > </ li >
143143< li > < a href ="#synchronous-asynchronous-execution "> Synchronous & Asynchronous Execution</ a > </ li >
144- < li > < a href ="#race-conditions " > Race Conditions </ a > </ li >
144+ < li > < a href ="#determinism " > Determinism </ a > </ li >
145145< li > < a href ="#spawning-threads "> Spawning Threads</ a > </ li >
146146< li > < a href ="#greenlet-state "> Greenlet State</ a > </ li >
147147< li > < a href ="#program-shutdown "> Program Shutdown</ a > </ li >
@@ -153,11 +153,15 @@ <h3 class="author">
153153< li > < a href ="#queues "> Queues</ a > </ li >
154154< li > < a href ="#groups-and-pools "> Groups and Pools</ a > </ li >
155155< li > < a href ="#locks-and-semaphores "> Locks and Semaphores</ a > </ li >
156+ < li > < a href ="#thread-locals "> Thread Locals</ a > < ul >
157+ < li > < a href ="#werkzeug "> Werkzeug</ a > </ li >
158+ </ ul >
159+ </ li >
156160< li > < a href ="#actors "> Actors</ a > </ li >
157161</ ul >
158162</ li >
159163< li > < a href ="#real-world-applications "> Real World Applications</ a > < ul >
160- < li > < a href ="#holding-side-effects " > Holding Side Effects </ a > </ li >
164+ < li > < a href ="#green-zeromq " > Green ZeroMQ </ a > </ li >
161165< li > < a href ="#wsgi-servers "> WSGI Servers</ a > </ li >
162166< li > < a href ="#long-polling "> Long Polling</ a > </ li >
163167< li > < a href ="#chat-server "> Chat Server</ a > </ li >
@@ -172,13 +176,14 @@ <h1 id="core">Core</h1>
172176concurrency is expected. The goal is to give you
173177the tools you need to get going with gevent and use it to solve
174178or speed up your applications today.</ p >
175- < p > The primary pattern provided by gevent is the < strong > Greenlet</ strong > , a
179+ < h2 id ="greenlets "> Greenlets</ h2 >
180+ < p > The primary pattern used in gevent is the < strong > Greenlet</ strong > , a
176181lightweight coroutine provided to Python as a C extension module.
177182Greenlets all run inside of the OS process for the main
178- program but are scheduled cooperatively by libev . This differs from
179- < a href =" http://docs.python.org/library/subprocess.html " > subprocceses </ a >
180- which are new processes are spawned by the OS. </ p >
181- < h2 id =" greenlets " > Greenlets </ h2 >
183+ program but are scheduled cooperatively. This differs from any of
184+ the real parallelism constructs provided by < code > multiprocessing </ code > or
185+ < code > multithreading </ code > libraries which do spin processes and posix threads
186+ which are truly parallel. </ p >
182187< h2 id ="synchronous-asynchronous-execution "> Synchronous & Asynchronous Execution</ h2 >
183188< p > The core idea of concurrency is that a larger task can be broken
184189down into a collection of subtasks whose operation does not
@@ -216,6 +221,51 @@ <h2 id="synchronous-asynchronous-execution">Synchronous & Asynchronous Execu
216221Emplict context switch to foo again
217222Implicit swtich switch back to bar
218223</ pre > </ code > </ p >
224+ < p > The real power of gevent comes when we use it for network and IO
225+ bound functions which can be cooperatively scheduled. Gevent has
226+ taken care of all the details to ensure that your network
227+ libraries will implictly yield their greenlet contexts whenever
228+ possible. I cannot stress enough what a powerful idiom this is.
229+ But maybe an example will illustrate.</ p >
230+ < pre > < code class ="python ">
231+ import time
232+ import gevent
233+ from gevent import select
234+
235+ start = time.time()
236+ tic = lambda: 'at %1.1f seconds' % (time.time() - start)
237+
238+ def gr1():
239+ # Busy waits for a second, but we don't want to stick around...
240+ print('Started Polling: ', tic())
241+ select.select([], [], [], 2)
242+ print('Ended Polling: ', tic())
243+
244+ def gr2():
245+ # Busy waits for a second, but we don't want to stick around...
246+ print('Started Polling: ', tic())
247+ select.select([], [], [], 2)
248+ print('Ended Polling: ', tic())
249+
250+ def gr3():
251+ print("Hey lets do some stuff while the greenlets poll, at", tic())
252+ gevent.sleep(1)
253+
254+ gevent.joinall([
255+ gevent.spawn(gr1),
256+ gevent.spawn(gr2),
257+ gevent.spawn(gr3),
258+ ])
259+ </ pre >
260+
261+ < p > </ code >
262+ < pre > < code class ="python ">
263+ Started Polling: at 0.0 seconds
264+ Started Polling: at 0.0 seconds
265+ Hey lets do some stuff while the greenlets poll, at at 0.0 seconds
266+ Ended Polling: at 2.0 seconds
267+ Ended Polling: at 2.0 seconds
268+ </ pre > </ code > </ p >
219269< p > A somewhat synthetic example defines a < code > task</ code > function
220270which is < em > non-deterministic</ em >
221271(i.e. its output is not guaranteed to give the same result for
@@ -264,13 +314,13 @@ <h2 id="synchronous-asynchronous-execution">Synchronous & Asynchronous Execu
264314Task 1 done
265315Task 2 done
266316Task 6 done
267- Task 9 done
268- Task 5 done
269- Task 0 done
317+ Task 3 done
318+ Task 4 done
270319Task 7 done
320+ Task 0 done
321+ Task 9 done
271322Task 8 done
272- Task 4 done
273- Task 3 done
323+ Task 5 done
274324</ pre > </ code > </ p >
275325< p > In the synchronous case all the tasks are run sequentially,
276326which results in the main programming < em > blocking</ em > (
@@ -328,7 +378,52 @@ <h2 id="synchronous-asynchronous-execution">Synchronous & Asynchronous Execu
328378</ code >
329379</ pre >
330380
331- < h2 id ="race-conditions "> Race Conditions</ h2 >
381+ < h2 id ="determinism "> Determinism</ h2 >
382+ < p > As mentioned previously, greenlets are deterministic. Given the
383+ same inputs and they always produce the same output. For example
384+ lets spread a task across a multiprocessing pool compared to a
385+ gevent pool.</ p >
386+ < pre > < code class ="python ">
387+ def echo(i):
388+ import time
389+ time.sleep(0.001)
390+ return i
391+
392+ # Non Deterministic Process Pool
393+
394+ from multiprocessing.pool import Pool
395+
396+ p = Pool(10)
397+ #run1 = [a for a in p.imap_unordered(echo, xrange(10))]
398+ #run2 = [a for a in p.imap_unordered(echo, xrange(10))]
399+ #run3 = [a for a in p.imap_unordered(echo, xrange(10))]
400+ #run4 = [a for a in p.imap_unordered(echo, xrange(10))]
401+
402+ #print( run1 == run2 == run3 == run4 )
403+
404+ # Deterministic Gevent Pool
405+
406+ from gevent.pool import Pool
407+
408+ p = Pool(10)
409+ run1 = [a for a in p.imap_unordered(echo, xrange(10))]
410+ run2 = [a for a in p.imap_unordered(echo, xrange(10))]
411+ run3 = [a for a in p.imap_unordered(echo, xrange(10))]
412+ run4 = [a for a in p.imap_unordered(echo, xrange(10))]
413+
414+ print( run1 == run2 == run3 == run4 )
415+ </ pre >
416+
417+ < p > </ code >
418+ < pre > < code class ="python ">
419+ True
420+ </ pre > </ code > </ p >
421+ < p > Even though gevent is normally deterministic, sources of
422+ non-determinism can creep into your program when you beging to
423+ interact with outside services such as sockets and files. Thus
424+ even though green threads are a form of "deterministic
425+ concurrency", they still can experience some of the smae problems
426+ that posix threads and processes experience.</ p >
332427< p > The perennial problem involved with concurrency is known as a
333428< em > race condition</ em > . Simply put is when two concurrent threads
334429/ processes depend on some shared resource but also attempt to
@@ -337,10 +432,9 @@ <h2 id="race-conditions">Race Conditions</h2>
337432general one should very much try to avoid race conditions since
338433they result program behavior which is globally
339434non-deterministic.*</ p >
340- < p > One approach to avoiding race conditions is to simply not
341- have any global < em > state</ em > shared between threads. To
342- communicate threads instead pass stateless messages between each
343- other.</ p >
435+ < p > The best approach to this is to simply avoid all global state all
436+ times. Global state and import-time side effects will always come
437+ back to bite you!</ p >
344438< h2 id ="spawning-threads "> Spawning Threads</ h2 >
345439< p > gevent provides a few wrappers around Greenlet initialization.
346440Some of the most common patterns are:</ p >
@@ -541,13 +635,12 @@ <h2 id="timeouts">Timeouts</h2>
541635 gevent.sleep(2)
542636
543637timer = Timeout(1).start()
544-
545638thread1 = gevent.spawn(wait)
546639
547640try:
548641 thread1.join(timeout=timer)
549642except Timeout:
550- print('Timed out')
643+ print('Thread 1 timed out')
551644
552645# --
553646
@@ -557,22 +650,22 @@ <h2 id="timeouts">Timeouts</h2>
557650try:
558651 thread2.get(timeout=timer)
559652except Timeout:
560- print('Timed out')
653+ print('Thread 2 timed out')
561654
562655# --
563656
564657try:
565658 gevent.with_timeout(1, wait)
566659except Timeout:
567- print('Timed out')
660+ print('Thread 3 timed out')
568661
569662</ pre >
570663
571664< p > </ code >
572665< pre > < code class ="python ">
573- Timed out
574- Timed out
575- Timed out
666+ Thread 1 timed out
667+ Thread 2 timed out
668+ Thread 3 timed out
576669</ pre > </ code > </ p >
577670< h1 id ="data-structures "> Data Structures</ h1 >
578671< h2 id ="events "> Events</ h2 >
@@ -718,8 +811,8 @@ <h2 id="queues">Queues</h2>
718811argument to allow for the queue to exit with the exception
719812< code > gevent.queue.Empty</ code > if no work can found within the
720813time frame of the Timeout.</ p >
721- < pre >
722- < code class =" python " > import gevent
814+ < pre > < code class =" python " >
815+ import gevent
723816from gevent.queue import Queue, Empty
724817
725818tasks = Queue(maxsize=3)
@@ -728,10 +821,10 @@ <h2 id="queues">Queues</h2>
728821 try:
729822 while True:
730823 task = tasks.get(timeout=1) # decrements queue size by 1
731- print 'Worker %s got task %s' % (n, task)
732- gevent.sleep(0.5 )
824+ print( 'Worker %s got task %s' % (n, task) )
825+ gevent.sleep(0)
733826 except Empty:
734- print 'Quitting time!'
827+ print( 'Quitting time!')
735828
736829def boss():
737830 """
@@ -741,23 +834,51 @@ <h2 id="queues">Queues</h2>
741834
742835 for i in xrange(1,10):
743836 tasks.put(i)
744- print 'Assigned all work in iteration 1'
837+ print( 'Assigned all work in iteration 1')
745838
746839 for i in xrange(10,20):
747840 tasks.put(i)
748- print 'Assigned all work in iteration 2'
841+ print( 'Assigned all work in iteration 2')
749842
750843gevent.joinall([
751844 gevent.spawn(boss),
752845 gevent.spawn(worker, 'steve'),
753846 gevent.spawn(worker, 'john'),
754847 gevent.spawn(worker, 'bob'),
755848])
756- </ code >
757849</ pre >
758850
851+ < p > </ code >
852+ < pre > < code class ="python ">
853+ Worker steve got task 1
854+ Worker john got task 2
855+ Worker bob got task 3
856+ Worker steve got task 4
857+ Worker bob got task 5
858+ Worker john got task 6
859+ Assigned all work in iteration 1
860+ Worker steve got task 7
861+ Worker john got task 8
862+ Worker bob got task 9
863+ Worker steve got task 10
864+ Worker bob got task 11
865+ Worker john got task 12
866+ Worker steve got task 13
867+ Worker john got task 14
868+ Worker bob got task 15
869+ Worker steve got task 16
870+ Worker bob got task 17
871+ Worker john got task 18
872+ Assigned all work in iteration 2
873+ Worker steve got task 19
874+ Quitting time!
875+ Quitting time!
876+ Quitting time!
877+ </ pre > </ code > </ p >
759878< h2 id ="groups-and-pools "> Groups and Pools</ h2 >
760879< h2 id ="locks-and-semaphores "> Locks and Semaphores</ h2 >
880+ < h2 id ="thread-locals "> Thread Locals</ h2 >
881+ < h3 id ="werkzeug "> Werkzeug</ h3 >
761882< h2 id ="actors "> Actors</ h2 >
762883< p > The actor model is a higher level concurrency model popularized
763884by the language Erlang. In short the main idea is that you have a
@@ -822,34 +943,7 @@ <h2 id="actors">Actors</h2>
822943</ pre >
823944
824945< h1 id ="real-world-applications "> Real World Applications</ h1 >
825- < h2 id ="holding-side-effects "> Holding Side Effects</ h2 >
826- < p > In this example we hold the side effects of executing an
827- arbitrary string, </ p >
828- < pre >
829- < code class ="python "> from gevent import Greenlet
830-
831- env = {}
832-
833- def run_code(code, env={}):
834- local = locals()
835- local.update(env)
836- exec(code, globals(), local)
837- return local
838-
839- while True:
840- code = raw_input('> ')
841-
842- g = Greenlet.spawn(run_code, code, env)
843- g.join() # block until code executes
844-
845- # If succesfull then pass the locals to the next command
846- if g.value:
847- env = g.get()
848- else:
849- print g.exception
850- </ code >
851- </ pre >
852-
946+ < h2 id ="green-zeromq "> Green ZeroMQ</ h2 >
853947< h2 id ="wsgi-servers "> WSGI Servers</ h2 >
854948< pre >
855949< code class ="python "> from gevent.pywsgi import WSGIServer
0 commit comments