Cara de moniato, are you ready ?

September 26, 2007 – 9:27 pm

Avui tinc la cara de moniato, després de passar gaire bé tota la tarda llegint documentació sobre com embebir python en un entorn multi thread i composat de múltiples intèrprets.

D'acord no és un tema trivial i cal estar versat en l'extensió de python amb llenguatge C per no perdre's en l'intent. Però la veritat es que tanta documentació heterogènia pot fer perdre els estreps a qualsevol.

No hi ha res que odii més que fer una cosa sense entendre-la, i en programació potser aquest és el cúmul dels desastres personals.

Anem a pams per posar la situació en joc, a veure si algú amb un coeficient intel.lectual superior al meu - cosa força fàcil, com a mínim ara - hem pot explicar per que nassos les coses són tan complicades.

Python com tots sabeu es un llenguatge d'scripting amb capacitat de ser embebit com a tal en un programa en C o C++,  extenent així la teva aplicació la capacitat d'executar scripts o sentencies amb codi pur de Python. Posteriorment i si tu vols pots exposar part de les dades del programa principal - semblant a una API - mitjançant metodologies semblants a la de extending python with C. Realitzar això té les seves problemàtiques i fins i tot hi ha aspectes que no son certament trivials - sistema de gargabe collector per exemple - pero en el fons només exigeix seguir un conjunt de regles de programació i estar una mica versat en la programació en C. Per cert molt recomenable una lectura al document Extending and Emedding the Python interpreter per enamorarte una mica més de la genialitat d'en Guido

On és doncs aquesta dificultat ? Basicament la trobem en el moment que barrejem threads i python, i el conjunt de funcions i sobretot el paradigma esperat.

Pero abans de presentar el paradigma caldria fer un repas al vocabulari que l'envolta :

  • GIL : Global Interpreter Lock, mecanisme de bloqueig global entre threads ( la negreta és molt important)
  • Interpreter : Representa un interpret executat per un thread
  • Sub Interpreter : Representa un sub interpret dintre un interpret executat per un thread
  • ThreadState : Estructura que representa un thread en python.

Un programa multithread que utiliza pyhton, en quina situació ? doncs en moltes, fem una ullada per exemple a mod_python !!!!

Python exposa un conjunt de funcions per a poder fer thread safe la teva aplicació, clar el paradigma d'una aplicació amb multiples threads i una d'un sol thread es molt diferent, diguis mutex, abraçades mortals etc .....

Entre aquest conjunt de funcions  tenim un grup dedicada a modificar el que sembla ser els contextos i per tant la cessió o apropiació de la CPU per un thread determinat.

En tenim un altre grup que tenen com a funcionalitat bloquejar el GIL o lliberar-lo.

I finalment tenim un grup de Macros dedicades a cedir la CPU en casos d'efectuar operacions de I/O i on el procés es pot quedar bloquejat momentaniament.

Be un cop presentat aquest tres grups de funcions, cal explicar que python com a tal no té una super estructura pròpia per controlar en quin context de thread un s'està executant i que per tant és responsabilitat de l'usuari aportar-la. Aquesta frase enrevessada i intel.ligible ens diu a la pràctica que nosaltres hem de dir a python quin es el thread que començara a executar-se a partir d'un moment determinat.

El codi que pot reflectir tot aquestes "palles mentals" seria una cosa aixins :

PyThreadState * mainThreadState = NULL;
// save a pointer to the main PyThreadState object
mainThreadState = PyThreadState_Get();
// release the lock
PyEval_ReleaseLock();
// get the global lock
PyEval_AcquireLock();
// get a reference to the PyInterpreterState
PyInterpreterState * mainInterpreterState = mainThreadState->interp;
// create a thread state object for this thread
PyThreadState * myThreadState = PyThreadState_New(mainInterpreterState);
// free the lock
PyEval_ReleaseLock();
// grab the global interpreter lock
PyEval_AcquireLock();
// swap in my thread state
PyThreadState_Swap(myThreadState);
// execute some python code
PyEval_SimpleString("import sys\n");
PyEval_SimpleString("sys.stdout.write('Hello from a C thread!\n')\n");
// clear the thread state
PyThreadState_Swap(NULL);
// release our hold on the global interpreter
PyEval_ReleaseLock();

Doncs be el codi anterior extret de LinuxJournal fa basicament tres coses

  1. Guarda en un punter el thread principal, tal com ja he comentat ens cal guardar aquesta informació
  2. Utiliza aquest punter per crear un nou interpret
  3. Intercanvia el control al segon thread mitjançant la funció Swap
  4. El thread fa alguna cosa com importar el modul amb sys i imprimir el tipic hellow world per pantalla
  5. Retorna el control mitjançant una altre crida a Swap pero amb el parametre a NULL

Tot i la obvietat del codi - oi ? - a mi hem queda un dubte molt important per contestar.

  1. Si hem tingut que salvar el context del thread principal per poder crear un  nou interpret com és que en el moment de restaurar-lo passen NULL a la funció SWAP

A part d'aixo crec que l'exemple es senzillament idiota, i no mostra de cap forma com s'explota la multi concurrència. Que creieu que passa amb el thread principal en el moment que cedim la cpu al segon thread ? doncs que no es tornar a  executar fins que no li tornem a cedir la cpu amb la crida màgica a SWAP

A part de la poca idionetat d l'exemple crec que no és ni de bon tros suficientment explicit i explicatiu.

I aquest no és lúnic problema, python dona a l'usuari la capacitat d'elegir entre 3 formes de fer tot aixo. La diferència entre elles ? doncs no ho se ni ho he trobat cap lloc on ho expliqui..

Per exemple per a poder Crear nous interprets tenim dues funcions al nostre abast

  1. PyThreadState_New
  2. Py_NewInterpreter()

De la primera ja n'hem parlat. De la segona he intentat llegir unes 30 vegades el següent paràgraf però no hi ha hagut manera d'entendre res.

" The return value points to the first thread state created in the new sub-interpreter. This thread state is made in the current thread state. Note that no actual thread is created; see the discussion of thread states below. If creation of the new interpreter is unsuccessful, NULL is returned; no exception is set since the exception state is stored in the current thread state and there may not be a current thread state. (Like all other Python/C API functions, the global interpreter lock must be held before calling this function and is still held when it returns; however, unlike most other Python/C API functions, there needn't be a current thread state on entry.)"

En que quedem es crea el cony de thread o no ?
Be ja paro, pero es que avui he tingut un dia molt complicat "literariament" parlant

Post a Comment

*
To prove you're a person (not a spam script), type the security word shown in the picture. Click on the picture to hear an audio file of the word.
Click to hear an audio file of the anti-spam word