[pyar] Bloqueo de Deferreds, deferToThread en Twisted y threads en Python, no los entiendo :(

Manuel Alejandro Cerón Estrada ceronman en gmail.com
Vie Oct 14 13:29:34 ART 2011


Hola Emiliano.

2011/10/14 Emiliano Dalla Verde Marcozzi <edvm en member.fsf.org>:
> Buenas querida lista!
> Les quiero mostrar un poco de codigo que es un sistemita de tareas
> periodicas con plugins:
> http://pastebin.com/gCrRpChr
>
> En el pastebin, en la parte superior figura el codigo del 'Task Manager'.
> Venimos usando deferToThread
> para lanzar tareas que pueden demorar tiempo y que no bloquee el resto de
> las tareas que debemos
> ejecutar. Luego de ver la charla de Lucio sobre Twisted, quise probar de
> hacer lo mismo con Deferreds,
> de ahi que meti un metodo 'toDefer' que acepta una funcion como parametro,
> crea un deferred, le atacheo
> la funcion callFunction y luego le seteo el valor del deferred al metodo que
> fue pasado como parametro
> para que se dispare el 'chain'. La cuestion, es que al crear una tarea que
> tiene un sleep de 10 segundos,
> puedo ver en el log como me bloquea la ejecucion de las siguientes tareas:
>
> 2011-10-14 11:23:02-0300 [-] starting to sleep ... zZzZz ...
> 2011-10-14 11:23:12-0300 [-] Whoops!, now i woke up!
> 2011-10-14 11:23:12-0300 [-] executed    blocking_task
> 2011-10-14 11:23:12-0300 [-] executed    ajustarhora
> 2011-10-14 11:23:12-0300 [-] executed    service_to_manual
> 2011-10-14 11:23:12-0300 [-] executed    ajustarhora
> 2011-10-14 11:23:13-0300 [-] executed    ajustarhora
> 2011-10-14 11:23:14-0300 [-] starting to sleep ... zZzZz ...
> 2011-10-14 11:23:24-0300 [-] Whoops!, now i woke up!
> 2011-10-14 11:23:24-0300 [-] executed    blocking_task
> 2011-10-14 11:23:24-0300 [-] executed    ajustarhora
> 2011-10-14 11:23:24-0300 [-] executed    service_to_manual
> 2011-10-14 11:23:24-0300 [-] executed    service_to_manual
> 2011-10-14 11:23:24-0300 [-] executed    ajustarhora
> 2011-10-14 11:23:25-0300 [-] executed    ajustarhora
> 2011-10-14 11:23:26-0300 [-] starting to sleep ... zZzZz ...
> 2011-10-14 11:23:36-0300 [-] Whoops!, now i woke up!
>
> Ahora bien, si uso o bien el metodo toThread o bien decoro la funcion
> blocking_task, no me bloquea
> el resto de las tareas que estan 'loopeando':
>
> 2011-10-14 11:31:58-0300 [-] starting to sleep ... zZzZz ...
> 2011-10-14 11:31:59-0300 [-] executed    ajustarhora
> 2011-10-14 11:32:00-0300 [-] executed    ajustarhora
> 2011-10-14 11:32:01-0300 [-] executed    ajustarhora
> 2011-10-14 11:32:02-0300 [-] executed    ajustarhora
> 2011-10-14 11:32:03-0300 [-] executed    ajustarhora
> 2011-10-14 11:32:03-0300 [-] starting to sleep ... zZzZz ...
> 2011-10-14 11:32:03-0300 [-] executed    deferToThread
> 2011-10-14 11:32:04-0300 [-] executed    ajustarhora
> 2011-10-14 11:32:05-0300 [-] executed    ajustarhora
> 2011-10-14 11:32:06-0300 [-] executed    service_to_manual
> 2011-10-14 11:32:06-0300 [-] executed    ajustarhora
> 2011-10-14 11:32:07-0300 [-] executed    ajustarhora
> 2011-10-14 11:32:08-0300 [-] executed    ajustarhora
> 2011-10-14 11:32:08-0300 [-] Whoops!, now i woke up!
> 2011-10-14 11:32:09-0300 [-] executed    ajustarhora
> 2011-10-14 11:32:09-0300 [-] executed    deferToThread
> 2011-10-14 11:32:09-0300 [-] starting to sleep ... zZzZz ...


> Mis preguntas son:
> 1) Puedo lograr el mismo efecto que 'deferToThread' pero usando solo
> Deferreds ? si es asi, en que estoy
> metiendo la pata ?

La respuesta es: no. O al menos no de la forma en la que los estas
planteando. El sistema de deferreds de Twisted no brinda ninguna clase
de paralelismo y corre de forma completamente secuencial. Sin embargo,
Twisted da cierta sensación de paralelismo porque está diseñado para
no tener que esperar por la entrada y salida de datos.

Lo que hace Twisted es correr un ciclo infinito, el cual simplemente
verifica si se cumplieron las condiciones para disparar un deferred y
si es el caso llama a la función callback asociada. Este ciclo se
conoce como reactor y es por eso que siempre en toda aplicación
Twisted debes correr un reactor. En términos muy simplificados, sería
algo como así:

while True:
    for d in allDeferreds:
        if d.ready:
            d.callback()

En tu ejemplo estas llamando a un sleep() en uno de los callbacks.
Como puedes ver, el reactor de Twisted no puede retomar el control
hasta que ese callback no haya sido ejecutado completamente. Esto
quiere decir que tu programa entero se quedará congelado hasta que
finalice el tiempo del sleep.

Cuando usas un framework como Twisted, debes tratar de tener callbacks
que sean muy cortos para no bloquear la aplicación completa. Una
solución, cuando se tienen tareas que toman tiempo es tratar de
partirlas en pequeñas tareas que tomen poco tiempo. Si esto
definitivamente no es posible, tienes que usar threads o procesos
diferentes.


> 2) Me surge la duda de que si los threads en Python son 'secuenciales' por
> el bloqueador GIL de python,
> como funciona pues entonces esto de que al usar deferToThread el sleep(10)
> de la tarea parece 'mandarse'
> en 'background' no bloqueandome el resto de las tareas y no asi usando los
> deferreds ? (posiblemente porque
> los este usando mal?) Me refiero a que da la "sensacion" (que palabra esta
> :P) de que se ejecutan en paralelo
> con deferToThread, como es este tema ?

El GIL de Python no significa que los threads se ejecuten de manera
secuencial, es decir uno después de otro. Lo que hace el GIL es evitar
que dos threads ejecuten una operación exactamente al mismo tiempo. De
esta forma los threads se ejecutan de una forma que es casi paralela:
un pedazo de uno, un pedazo de otro, etc etc etc. El inconveniente es
que si dos procesadores diferentes quieres ejecutar algo exactamente
al mismo tiempo, el GIT lo impide. Este tipo de semi paralelismo es
similar a lo que hacen los sistemas operativos cuando necesitan
brindar multitarea pero sólo tienen un procesador que sólo puede hacer
una cosa a la vez.

> 3) Tengo de oido que usar threads es una mala idea, esto es asi con
> deferToThread?

No es que sea mala idea usar los threads, es sólo que son realmente
complicados de usar. Los programas que usan threads por lo general son
difíciles de depurar, porque rompen el esquema de algoritmo secuencial
que tenemos en la mente.

A veces es necesario usar threads. Lo bueno es que la razón por la
cual existe el GIL es para simplificar muchos de los problemas que
existen con ellos. De esa manera no va a ser tan difícil usarlos.

> 4) {orque existen los threads en Python, en el caso de que se ejecuten
> secuencialmente uno luego del otro por esto del GIL ?

Esto ya lo expliqué arriba.

Saludos.

Manuel.



More information about the pyar mailing list