@@ -157,10 +157,7 @@ <h3 class="author">
157157< li > < a href ="#queues "> Queues</ a > </ li >
158158< li > < a href ="#groups-and-pools "> Groups and Pools</ a > </ li >
159159< li > < a href ="#locks-and-semaphores "> Locks and Semaphores</ a > </ li >
160- < li > < a href ="#thread-locals "> Thread Locals</ a > < ul >
161- < li > < a href ="#werkzeug "> Werkzeug</ a > </ li >
162- </ ul >
163- </ li >
160+ < li > < a href ="#thread-locals "> Thread Locals</ a > </ li >
164161< li > < a href ="#actors "> Actors</ a > </ li >
165162</ ul >
166163</ li >
@@ -205,20 +202,21 @@ <h2 id="greenlets">Greenlets</h2>
205202< p > The primary pattern used in gevent is the < strong > Greenlet</ strong > , a
206203lightweight coroutine provided to Python as a C extension module.
207204Greenlets all run inside of the OS process for the main
208- program but are scheduled cooperatively. This differs from any of
209- the real parallelism constructs provided by < code > multiprocessing</ code > or
210- < code > multithreading</ code > libraries which do spin processes and POSIX threads
211- which are truly parallel.</ p >
205+ program but are scheduled cooperatively. </ p >
206+ < blockquote >
207+ < p > Only one greenlet is ever running at any given time.</ p >
208+ </ blockquote >
209+ < p > This differs from any of the real parallelism constructs provided by
210+ < code > multiprocessing</ code > or < code > threading</ code > libraries which do spin processes
211+ and POSIX threads which are scheduled by the operating system and
212+ are truly parallel.</ p >
212213< h2 id ="synchronous-asynchronous-execution "> Synchronous & Asynchronous Execution</ h2 >
213- < p > The core idea of concurrency is that a larger task can be broken
214- down into a collection of subtasks whose operation does not
215- depend on the other tasks and thus can be run
216- < em > asynchronously</ em > instead of one at a time
217- < em > synchronously</ em > . A switch between the two
218- executions is known as a < em > context switch</ em > .</ p >
219- < p > A context switch in gevent is done through
220- < em > yielding</ em > . In this case example we have
221- two contexts which yield to each other through invoking
214+ < p > The core idea of concurrency is that a larger task can be broken down
215+ into a collection of subtasks whose and scheduled to run simultaneously
216+ or < em > asynchronously</ em > , instead of one at a time or < em > synchronously</ em > . A
217+ switch between the two subtasks is known as a < em > context switch</ em > .</ p >
218+ < p > A context switch in gevent is done through < em > yielding</ em > . In this case
219+ example we have two contexts which yield to each other through invoking
222220< code > gevent.sleep(0)</ code > .</ p >
223221< pre > < code class ="python ">
224222import gevent
@@ -255,6 +253,8 @@ <h2 id="synchronous-asynchronous-execution">Synchronous & Asynchronous Execu
255253libraries will implicitly yield their greenlet contexts whenever
256254possible. I cannot stress enough what a powerful idiom this is.
257255But maybe an example will illustrate.</ p >
256+ < p > In this case the < code > select()</ code > function is normally a blocking
257+ call that polls on various file descriptors.</ p >
258258< pre > < code class ="python ">
259259import time
260260import gevent
@@ -294,7 +294,7 @@ <h2 id="synchronous-asynchronous-execution">Synchronous & Asynchronous Execu
294294Ended Polling: at 2.0 seconds
295295Ended Polling: at 2.0 seconds
296296</ pre > </ code > </ p >
297- < p > A somewhat synthetic example defines a < code > task</ code > function
297+ < p > Another somewhat synthetic example defines a < code > task</ code > function
298298which is < em > non-deterministic</ em >
299299(i.e. its output is not guaranteed to give the same result for
300300the same inputs). In this case the side effect of running the
@@ -339,16 +339,16 @@ <h2 id="synchronous-asynchronous-execution">Synchronous & Asynchronous Execu
339339Task 8 done
340340Task 9 done
341341Asynchronous:
342- Task 0 done
343- Task 2 done
342+ Task 9 done
344343Task 6 done
345- Task 5 done
344+ Task 3 done
346345Task 4 done
347- Task 7 done
348346Task 8 done
349- Task 9 done
347+ Task 5 done
350348Task 1 done
351- Task 3 done
349+ Task 7 done
350+ Task 0 done
351+ Task 2 done
352352</ pre > </ code > </ p >
353353< p > In the synchronous case all the tasks are run sequentially,
354354which results in the main programming < em > blocking</ em > (
@@ -407,10 +407,10 @@ <h2 id="synchronous-asynchronous-execution">Synchronous & Asynchronous Execu
407407</ pre >
408408
409409< h2 id ="determinism "> Determinism</ h2 >
410- < p > As mentioned previously, greenlets are deterministic. Given the
411- same inputs and they always produce the same output. For example
412- lets spread a task across a multiprocessing pool compared to a
413- gevent pool.</ p >
410+ < p > As mentioned previously, greenlets are deterministic. Given the same
411+ configuration of greenlets and the same set of inputs and they always
412+ produce the same output. For example lets spread a task across a
413+ multiprocessing pool compared to a gevent pool.</ p >
414414< pre >
415415< code class ="python ">
416416import time
@@ -910,9 +910,197 @@ <h2 id="queues">Queues</h2>
910910Quitting time!
911911</ pre > </ code > </ p >
912912< h2 id ="groups-and-pools "> Groups and Pools</ h2 >
913+ < p > A group is a collection of running greenlets which are managed
914+ and scheduled together as group. It also doubles as parallel
915+ dispatcher that mirrors the Python < code > multiprocessing</ code > library.</ p >
916+ < pre > < code class ="python ">
917+ import gevent
918+ from gevent.pool import Group
919+
920+ def talk(msg):
921+ for i in xrange(3):
922+ print(msg)
923+
924+ g1 = gevent.spawn(talk, 'bar')
925+ g2 = gevent.spawn(talk, 'foo')
926+ g3 = gevent.spawn(talk, 'fizz')
927+
928+ group = Group()
929+ group.add(g1)
930+ group.add(g2)
931+ group.join()
932+
933+ group.add(g3)
934+ group.join()
935+ </ pre >
936+
937+ < p > </ code >
938+ < pre > < code class ="python ">
939+ bar
940+ bar
941+ bar
942+ foo
943+ foo
944+ foo
945+ fizz
946+ fizz
947+ fizz
948+ </ pre > </ code > </ p >
949+ < p > This is very usefull for managing groups of asynchronous tasks
950+ that.</ p >
951+ < p > As mentioned above Group also provides an API for dispatching
952+ jobs to grouped greenlets and collecting their results in various
953+ ways.</ p >
954+ < pre > < code class ="python ">
955+ import gevent
956+ from gevent import getcurrent
957+ from gevent.pool import Group
958+
959+ group = Group()
960+
961+ def hello_from(n):
962+ print('Size of group', len(group))
963+ print('Hello from Greenlet %s' % id(getcurrent()))
964+
965+ group.map(hello_from, xrange(3))
966+
967+ def intensive(n):
968+ gevent.sleep(3 - n)
969+ return 'task', n
970+
971+ print('Ordered')
972+
973+ ogroup = Group()
974+ for i in ogroup.imap(intensive, xrange(3)):
975+ print(i)
976+
977+ print('Unordered')
978+
979+ igroup = Group()
980+ for i in igroup.imap_unordered(intensive, xrange(3)):
981+ print(i)
982+
983+ </ pre >
984+
985+ < p > </ code >
986+ < pre > < code class ="python ">
987+ Size of group 3
988+ Hello from Greenlet 140728641091376
989+ Size of group 3
990+ Hello from Greenlet 140728641090736
991+ Size of group 3
992+ Hello from Greenlet 140728641091696
993+ Ordered
994+ ('task', 0)
995+ ('task', 1)
996+ ('task', 2)
997+ Unordered
998+ ('task', 2)
999+ ('task', 1)
1000+ ('task', 0)
1001+ </ pre > </ code > </ p >
1002+ < p > A pool is a structure designed for handling dynamic numbers of
1003+ greenlets which need to be concurrency-limited. This is often
1004+ desirable in cases where one wants to do many network or IO bound
1005+ tasks in parallel.</ p >
1006+ < pre > < code class ="python ">
1007+ import gevent
1008+ from gevent import getcurrent
1009+ from gevent.pool import Pool
1010+
1011+ pool = Pool(2)
1012+
1013+ def hello_from(n):
1014+ print('Size of pool', len(pool))
1015+
1016+ pool.map(hello_from, xrange(3))
1017+ </ pre >
1018+
1019+ < p > </ code >
1020+ < pre > < code class ="python ">
1021+ Size of pool 2
1022+ Size of pool 2
1023+ Size of pool 1
1024+ </ pre > </ code > </ p >
1025+ < p > Often when building gevent driven services one will center the
1026+ entire service around a pool structure. An example might be a
1027+ class which polls on various sockets.</ p >
1028+ < pre >
1029+ < code class ="python "> from gevent.pool import Pool
1030+
1031+ class SocketPool(object):
1032+
1033+ def __init__(self):
1034+ self.pool = Pool(1000)
1035+ self.pool.start()
1036+
1037+ def listen(self, socket):
1038+ while True:
1039+ socket.recv()
1040+
1041+ def add_handler(self, socket):
1042+ if self.pool.full():
1043+ raise Exception("At maximum pool size")
1044+ else:
1045+ self.pool.spawn(self.listen, socket)
1046+
1047+ def shutdown(self):
1048+ self.pool.kill()
1049+
1050+ </ code >
1051+ </ pre >
1052+
9131053< h2 id ="locks-and-semaphores "> Locks and Semaphores</ h2 >
1054+ < p > A semaphore is a low level synchronization primitive that allows
1055+ greenlets to coordinate and limit concurrent access or execution. A
1056+ semaphore exposes two methods, < code > acquire</ code > and < code > release</ code > The
1057+ difference between the number of times and a semaphore has been
1058+ acquired and released is called the bound of the semaphore. If a
1059+ semaphore bound reaches 0 it will block until another greenlet
1060+ releases its acquisition.</ p >
1061+ < pre > < code class ="python ">
1062+ from gevent import sleep
1063+ from gevent.pool import Pool
1064+ from gevent.coros import BoundedSemaphore
1065+
1066+ sem = BoundedSemaphore(2)
1067+
1068+ def worker1(n):
1069+ sem.acquire()
1070+ print('Worker %i acquired semaphore' % n)
1071+ sleep(0)
1072+ sem.release()
1073+ print('Worker %i released semaphore' % n)
1074+
1075+ def worker2(n):
1076+ with sem:
1077+ print('Worker %i acquired semaphore' % n)
1078+ sleep(0)
1079+ print('Worker %i released semaphore' % n)
1080+
1081+ pool = Pool()
1082+ pool.map(worker1, xrange(0,2))
1083+ pool.map(worker2, xrange(3,6))
1084+ </ pre >
1085+
1086+ < p > </ code >
1087+ < pre > < code class ="python ">
1088+ Worker 0 acquired semaphore
1089+ Worker 1 acquired semaphore
1090+ Worker 0 released semaphore
1091+ Worker 1 released semaphore
1092+ Worker 3 acquired semaphore
1093+ Worker 4 acquired semaphore
1094+ Worker 3 released semaphore
1095+ Worker 4 released semaphore
1096+ Worker 5 acquired semaphore
1097+ Worker 5 released semaphore
1098+ </ pre > </ code > </ p >
1099+ < p > A semaphore with bound of 1 is known as a Lock. it provides
1100+ exclusive execution to one greenlet. They are often used to
1101+ ensure that resources are only in use at one time in the context
1102+ of a program.</ p >
9141103< h2 id ="thread-locals "> Thread Locals</ h2 >
915- < h3 id ="werkzeug "> Werkzeug</ h3 >
9161104< h2 id ="actors "> Actors</ h2 >
9171105< p > The actor model is a higher level concurrency model popularized
9181106by the language Erlang. In short the main idea is that you have a
0 commit comments