====== Greenlet & gevent ====== ===== Greenlet ===== Un greenlet es un micro-thread sin "organización implícita", esto significa que contraolas exactamente cuando se está ejecutando y adecuar su uso. \\ El uso de greenlets es parecido al de una pila de capas. La más baja es la función inicial que fue llamada, y las demás son greenlets actualmente pausados. Se irá indicando cuando se quiere trabajar con uno o con otro (switching). \\ Cuando se crea un greenlet se inicia una pila vacía y se ejecuta en ella una función específica. \\ Los greenlet pueden morir al acabar su ejecución o debido a una excepción no controlada. \\ La salida del siguiente código es: 12, 56, 34. Nunca aparecerá el 78 a no ser que se le añada un ''gr2.switch()'' después de imprimir 34. from greenlet import greenlet def test1(): print 12 gr2.switch() print 34 def test2(): print 56 gr1.switch() print 78 gr1 = greenlet(test1) gr2 = greenlet(test2) gr1.switch() ==== Muerte de un greenlet ==== Cada greenlet tiene un greenlet "padre" el cual es donde fue creado. El padre también es donde la ejecución del greenlet continua cuando el greenlet muere. En el código de arriba tanto gr1 como gr2 tienen ''main'' como el greenlet padre, cuando la ejecución de alguno de los dos acaba se vuelve al main. ==== Creación ==== ==== Switching ==== Switches entre greenlets ocurren cuando el método switch del greenlet es llamado. Se pasará al greenlet al cual ''switch()'' ha sido llamado. \\ Se puede pasar información def test1(x, y):< z = gr2.switch(x+y) print z def test2(u): print u gr1.switch(42) gr1 = greenlet(test1) gr2 = greenlet(test2) gr1.switch("hello", " world") Imprimirá "hello world" y 42. ==== Métodos ==== * ''switch(*args, **kwargs)'', cambia la ejecución del greenlet. * ''run'', es un atributo que deja de existir cuando el greenlet ha sido iniciado. * ''parent'', el greenlet padre. * ''gr_frame'', la capa de más arriba (o ''None''). * ''dead'', ''True'' si el greenlet está muerto. Podemos hacer un cast a ''bool'' de una variable greenlet, esta será true si el greenlet está activo. ===== gevent ===== * Para instalarlo has de tener instalado en el operativo la librería ''libevent-dev'' * [[http://www.gevent.org/contents.html|Documentación]] Es una librería basada en concurrencia por greenlets para desarrollar aplicaciones asíncronas, puede ser aprovechado es las funciones de network. \\ El siguiente código: import gevent def foo(): print('Running in foo') gevent.sleep(0) print('Explicit context switch to foo again') def bar(): print('Explicit context to bar') gevent.sleep(0) print('Implicit context switch back to bar') gevent.joinall([ gevent.spawn(foo), gevent.spawn(bar), ]) ... Se ejecuta de la siguiente forma: \\ {{:fw:others:flow.gif?direct&200|}} ==== Méotodos ==== * ''gevent.spawn()'' * ''gevent.joinall([gevents])'', detiene la ejecución del programa hasta que los greenlets no han acabado. ==== Monkey patching ==== Es una técnica que consiste en substituir llamadas a elementos del sistema que son bloqueantes por otras que no lo son y, de esa forma, adaptar código antiguo al nuevo. \\ Por ejemplo existe una substitución a ''sleep''. La de la librería básica bloquea el event loop mientras que ''gevent.spleep'' no . \\ Módulos que han sido substituidos: //socket//, //ssl//, //os//, //time//, //select//, //thread // y //threading//. ===== Ejemplos ===== === Métodos síncronos y asíncronos === import gevent import random def task(pid): """ Some non-deterministic task """ gevent.sleep(random.randint(0,2)*0.001) print('Task', pid, 'done') def synchronous(): for i in range(1,10): task(i) def asynchronous(): threads = [gevent.spawn(task, i) for i in xrange(10)] gevent.joinall(threads) print('Synchronous:') synchronous() print('Asynchronous:') asynchronous() Synchronous: Task 1 done Task 2 done Task 3 done Task 4 done Task 5 done Task 6 done Task 7 done Task 8 done Task 9 done Asynchronous: Task 1 done Task 2 done Task 7 done Task 6 done Task 5 done Task 0 done Task 3 done Task 8 done Task 9 done Task 4 done === Acceso a red asíncronamente === import gevent.monkey gevent.monkey.patch_socket() import gevent import urllib2 import simplejson as json def fetch(pid): response = urllib2.urlopen('http://json-time.appspot.com/time.json') result = response.read() json_result = json.loads(result) datetime = json_result['datetime'] print 'Process ', pid, datetime return json_result['datetime'] def synchronous(): for i in range(1,10): fetch(i) def asynchronous(): threads = [] for i in range(1,10): threads.append(gevent.spawn(fetch, i)) gevent.joinall(threads) print 'Synchronous:' synchronous() print 'Asynchronous:' asynchronous() ===== Notas ===== ==== ZeroMQ ==== Para hacer el paquete ''pyzmq'' compatible con los greenlets instalaremos el paquete ''gevent-zeromq''. A partir de entonces instanciaremos zeromq de la siguiente forma: from gevent_zeromq import zmq