Skip to content

Commit 87d79f1

Browse files
committed
Updated.
1 parent 5b66683 commit 87d79f1

File tree

2 files changed

+261
-110
lines changed

2 files changed

+261
-110
lines changed

index.html

Lines changed: 154 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -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 &amp; 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>
172176
concurrency is expected. The goal is to give you
173177
the tools you need to get going with gevent and use it to solve
174178
or 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
176181
lightweight coroutine provided to Python as a C extension module.
177182
Greenlets 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 &amp; Asynchronous Execution</h2>
183188
<p>The core idea of concurrency is that a larger task can be broken
184189
down into a collection of subtasks whose operation does not
@@ -216,6 +221,51 @@ <h2 id="synchronous-asynchronous-execution">Synchronous &amp; Asynchronous Execu
216221
Emplict context switch to foo again
217222
Implicit 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
220270
which 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 &amp; Asynchronous Execu
264314
Task 1 done
265315
Task 2 done
266316
Task 6 done
267-
Task 9 done
268-
Task 5 done
269-
Task 0 done
317+
Task 3 done
318+
Task 4 done
270319
Task 7 done
320+
Task 0 done
321+
Task 9 done
271322
Task 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,
276326
which results in the main programming <em>blocking</em> (
@@ -328,7 +378,52 @@ <h2 id="synchronous-asynchronous-execution">Synchronous &amp; 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>
337432
general one should very much try to avoid race conditions since
338433
they result program behavior which is globally
339434
non-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.
346440
Some of the most common patterns are:</p>
@@ -541,13 +635,12 @@ <h2 id="timeouts">Timeouts</h2>
541635
gevent.sleep(2)
542636

543637
timer = Timeout(1).start()
544-
545638
thread1 = gevent.spawn(wait)
546639

547640
try:
548641
thread1.join(timeout=timer)
549642
except Timeout:
550-
print('Timed out')
643+
print('Thread 1 timed out')
551644

552645
# --
553646

@@ -557,22 +650,22 @@ <h2 id="timeouts">Timeouts</h2>
557650
try:
558651
thread2.get(timeout=timer)
559652
except Timeout:
560-
print('Timed out')
653+
print('Thread 2 timed out')
561654

562655
# --
563656

564657
try:
565658
gevent.with_timeout(1, wait)
566659
except 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>
718811
argument to allow for the queue to exit with the exception
719812
<code>gevent.queue.Empty</code> if no work can found within the
720813
time frame of the Timeout.</p>
721-
<pre>
722-
<code class="python">import gevent
814+
<pre><code class="python">
815+
import gevent
723816
from gevent.queue import Queue, Empty
724817

725818
tasks = 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

736829
def 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

750843
gevent.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
763884
by 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

Comments
 (0)